├── LICENSE ├── README.md ├── content ├── README.md ├── basics.py ├── changes.py ├── errors.py ├── files │ ├── dynamic_facts.sh │ ├── feature_flags.json │ ├── foo2.txt │ ├── foo3.txt │ └── star_trek.yml ├── hello.py ├── inventory │ └── inventory.toml ├── push_demo.py ├── templates │ └── foo.j2 ├── user_facts.py └── var_scoping.py ├── defaults.toml └── module_docs ├── README.md ├── directory.py ├── echo.py ├── file.py ├── files └── foo.txt ├── group.py ├── package.py ├── service.py ├── shell.py ├── templates └── foo.txt.j2 └── user.py /LICENSE: -------------------------------------------------------------------------------- 1 | Opsmop is Apache 2 Licensed software. 2 | 3 | Opsmop demo content is public domain. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OpsMop Demo 2 | =========== 3 | 4 | WARNING: the opsmop language is now in flux prior to a 1.0 hardening of the language spec. This should be rresolved in a few weeks. This note is as of January 15, 2019. 5 | 6 | License 7 | ======= 8 | 9 | OpsMop is Apache 2 Licensed, (C) Michael DeHaan LLC, 2018. 10 | 11 | Content in this repository is public domain. 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /content/README.md: -------------------------------------------------------------------------------- 1 | Executable Demos 2 | ================ 3 | 4 | This directory contains executable demos referenced in the OpsMop documentation at [opsmop.io](https://opsmop.io). 5 | 6 | Please also be sure to read the 'module_docs' directory, as these examples are equally useful. 7 | The module_docs are used to automatically generate the main module documentation and are also executable! 8 | 9 | 10 | -------------------------------------------------------------------------------- /content/basics.py: -------------------------------------------------------------------------------- 1 | from opsmop.core.easy import * 2 | 3 | # cd thisdir 4 | # python3 basics.py --local --check 5 | # python3 basics.py --local --apply 6 | 7 | class WebServers(Role): 8 | 9 | def set_variables(self): 10 | return dict(what='bar', code=1234) 11 | 12 | def main(self): 13 | 14 | # this is not a real example and is just a bunch of resources thrown together 15 | # illustrating syntax, basic variables, and change detection. 16 | # a more complete example installing a popular-application may be added later. 17 | 18 | p1 = File(name="/tmp/opsmop-foo.txt", from_content="Hello World!") 19 | File(name=T("/tmp/opsmop-{{ what }}.txt"), from_content="Hello World 2!") # a dynamic path 20 | bar_contents = Shell("cat /tmp/opsmop-bar.txt") 21 | Echo("{{ bar_contents.data }}") 22 | p2 = Package(name="cowsay", method="brew") 23 | if p1.changed: 24 | Service(name='foo', restarted=True) 25 | if p2.changed: 26 | Service(name='nginx', restarted=True) 27 | 28 | 29 | class Demo(Policy): 30 | 31 | def set_variables(self): 32 | return dict(asdf = 'jkl;') 33 | 34 | def set_roles(self): 35 | return Roles( 36 | WebServers(name='webservers') 37 | ) 38 | 39 | if __name__ == '__main__': 40 | Cli(Demo()) 41 | 42 | -------------------------------------------------------------------------------- /content/changes.py: -------------------------------------------------------------------------------- 1 | # a simple demo showing change controls 2 | # 3 | # normally a shell command will always notify a handler 4 | # how do we control that based on return codes or output? 5 | 6 | from opsmop.core.easy import * 7 | import random 8 | 9 | class Main(Role): 10 | 11 | def set_variables(self): 12 | return dict(a=5, b=6, c=True) 13 | 14 | def main(self): 15 | 16 | def danger(x): 17 | return x.rc == 7 or 'fence power down' in x.data 18 | 19 | s1 = Shell("echo 'fence power down'", ignore_errors=True, changed_when=danger) 20 | s2 = Shell("echo 'all good'", ignore_errors=True, changed_when=danger) 21 | 22 | if s1.changed or s2.changed: 23 | Echo("Sound the alarm!") 24 | 25 | class Demo(Policy): 26 | 27 | def set_variables(self): 28 | return dict() 29 | 30 | def set_roles(self): 31 | return Roles( 32 | Main() 33 | ) 34 | 35 | if __name__ == '__main__': 36 | Cli(Demo()) 37 | 38 | 39 | -------------------------------------------------------------------------------- /content/errors.py: -------------------------------------------------------------------------------- 1 | # a simple demo showing how to ignore and manipulate the error status of the shell command 2 | 3 | from opsmop.core.easy import * 4 | 5 | class Main(Role): 6 | 7 | def set_variables(self): 8 | return dict(a=5, b=6, c=True) 9 | 10 | def main(self): 11 | 12 | # a non-zero return code, so this will pass 13 | Shell("/usr/bin/true"), 14 | 15 | # explicitly ignoring errors 16 | x = Shell("/usr/bin/false", ignore_errors=True) 17 | Echo("the result was {{ x }} - {{ x.rc }} - {{ x.data }}") 18 | if x.rc == 42: 19 | # this won't happen, but you can raise arbitrary exceptions 20 | raise Exception("failed") 21 | 22 | # conditionally ignoring errors, in a trivial way 23 | Shell("/usr/bin/false", failed_when=False) 24 | 25 | # conditionally deciding when to ignore errors 26 | Shell("/usr/bin/false", failed_when=lambda o: o.rc == 0) 27 | 28 | # considering the output to decide when to fail, also showing the condition assigned to a variable 29 | Shell("echo hi", failed_when=lambda o: 'hi' not in o.data) 30 | 31 | # exception handling 32 | try: 33 | Shell("/usr/bin/false") 34 | except: 35 | Echo("exception recovered") 36 | 37 | Echo("this next step will fail") 38 | 39 | Shell("/usr/bin/false") 40 | 41 | class Demo(Policy): 42 | 43 | def set_variables(self): 44 | return dict() 45 | 46 | def set_roles(self): 47 | return Roles( 48 | Main() 49 | ) 50 | 51 | if __name__ == '__main__': 52 | Cli(Demo()) 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /content/files/dynamic_facts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # this is an arbitrary example of how to write a dynamic executable fact 4 | # see user_facts.py in this repo for an example of usage. 5 | 6 | DATE=`date` 7 | 8 | echo '{ "user_date" : "${date}" }' 9 | 10 | -------------------------------------------------------------------------------- /content/files/feature_flags.json: -------------------------------------------------------------------------------- 1 | { 2 | "feature_flags" : { 3 | "ff01" : true, 4 | "ff02" : false 5 | } 6 | } -------------------------------------------------------------------------------- /content/files/foo2.txt: -------------------------------------------------------------------------------- 1 | FOO2 2 | -------------------------------------------------------------------------------- /content/files/foo3.txt: -------------------------------------------------------------------------------- 1 | FOO3 2 | -------------------------------------------------------------------------------- /content/files/star_trek.yml: -------------------------------------------------------------------------------- 1 | warp_factor: 9 2 | phasers: "stun" 3 | photon_torpedos: "spread pattern" 4 | tea: "Earl Grey, Hot" 5 | crew: 6 | officers: 7 | security: 'Worf' 8 | science: 'Data' 9 | captain: 'Picard' 10 | -------------------------------------------------------------------------------- /content/hello.py: -------------------------------------------------------------------------------- 1 | # 2 | # A very minimal language example 3 | # 4 | # USAGE: 5 | # 6 | # bin/opsmop check hello.py 7 | # bin/opsmop apply hello.py 8 | # 9 | 10 | from opsmop.core.easy import * 11 | 12 | class HelloRole(Role): 13 | 14 | def set_variables(self): 15 | return dict(program='OpsMop') 16 | 17 | def main(self): 18 | 19 | msg = T("Hello {{ program }} World! {{ say}}!") 20 | 21 | f1 = File(name="/tmp/foo.txt", from_content=msg) 22 | 23 | if f1.changed: 24 | Echo("file has changed") 25 | 26 | # ----------------- 27 | 28 | class Hello(Policy): 29 | 30 | def set_variables(self): 31 | return dict() 32 | 33 | def set_roles(self): 34 | return Roles(HelloRole()) 35 | 36 | if __name__ == '__main__': 37 | Cli(Hello(say='Congratulations')) 38 | -------------------------------------------------------------------------------- /content/inventory/inventory.toml: -------------------------------------------------------------------------------- 1 | # inventory is a concept only needed by pull mode 2 | # (this is a work in progress) 3 | 4 | # define a group like this... 5 | # * each entry is a hostname and a set of variables to apply to that host 6 | # * one gotcha: in TOML, IPs need to be quoted 7 | 8 | [groups.webservers.hosts] 9 | example1234 = "opsmop_host=192.168.48.143 z=3002" 10 | "127.0.0.1" = "" 11 | 12 | [groups.group1.hosts] 13 | hostname1 = "d=7 e=8 f=9 z=3001" 14 | "127.0.0.1" = "d=7 e=8 f=10 z=3000" 15 | 16 | # lets define another group. 17 | # variables are optional, to avoid setting variables use a blank string 18 | 19 | [groups.group2.hosts] 20 | hostname2 = "" 21 | hostname3 = "" 22 | 23 | # variables can also be applied at the group level, like this... 24 | # these variables can be highly structured but here we are just showing basics 25 | 26 | [groups.group2.vars] 27 | a=1 28 | b=2 29 | c=3 30 | 31 | [groups.group1.vars] 32 | a=4 33 | b=5 34 | c=6 35 | 36 | # here is an alternate way to define host variables 37 | # it probably won't be used very much because it is somewhat verbose 38 | # but other inventory sources might not care 39 | 40 | [hosts.hostname1.vars] 41 | g=10 42 | h=11 43 | 44 | # the all group is magic and assigns variables to all groups 45 | # the variable 'opsmop_host' is special and overrides the contact address of a host name 46 | # we are using it to simulate a larger fleet with only one box. 47 | 48 | [groups.all.vars] 49 | opsmop_host="127.0.0.1" 50 | opsmop_python_path="/usr/bin/python3" 51 | 52 | -------------------------------------------------------------------------------- /content/push_demo.py: -------------------------------------------------------------------------------- 1 | # push mode support for OpsMop is still under development. 2 | # here is an example of what inventory and push mode might look like 3 | 4 | from opsmop.core.easy import * 5 | 6 | # cd thisdir 7 | # python3 push_demo.py --push --check 8 | # python3 push_demo.py --push --apply 9 | 10 | inventory = None 11 | 12 | class BaseRole(Role): 13 | 14 | def ssh_as(self): 15 | # most users will want to omit this method and instead have a ~/.opsmop/defaults.toml 16 | return ('mpdehaan', None) 17 | 18 | def sudo(self): 19 | return True 20 | 21 | def sudo_as(self): 22 | # if required, a password can be read from ~/.opsmop/defaults.toml 23 | return ('root', None) 24 | 25 | class WebServers(BaseRole): 26 | 27 | def inventory(self): 28 | return inventory.filter(groups='webservers*') 29 | 30 | def set_variables(self): 31 | return dict(x=5, y=6, z=7) 32 | 33 | def main(self): 34 | 35 | f1 = File("/tmp/foo3.txt", from_content="Hey!") 36 | f2 = File("/tmp/foo1.txt", from_template="templates/foo.j2") 37 | Shell("uname -a", changed_when=False) 38 | 39 | if f1.changed: 40 | Echo("one") 41 | Echo("two") 42 | if f2.changed: 43 | Echo("three") 44 | 45 | def allow_fileserving_paths(self): 46 | # this is optional, the default is just '.' which means where this file is 47 | # if you want to allow things like from_file='/opt/foo/bar.txt' 48 | # return [ '.', '/opt/foo' ] 49 | return [ '.' ] 50 | 51 | class AnotherRole(BaseRole): 52 | 53 | # this example demonstrates load balancing hooks 54 | def should_contact(self, host): 55 | # optional. this is like should_process_when but executes before we try to connect to the host 56 | return True 57 | 58 | def after_contact(self, host): 59 | # optional. hook that can be used to put a node back in a load balancer, etc 60 | # print("balance %s" % host.hostname()) 61 | pass 62 | 63 | def before_contact(self, host): 64 | # optional hook. that can be used to pull a node out of a load balancer, etc 65 | # print("unbalance %s" % host.hostname()) 66 | pass 67 | 68 | def serial(self): 69 | # number of hosts to process at once 70 | return 5 71 | 72 | def inventory(self): 73 | return inventory.filter(groups=['webservers*','dbservers*']) 74 | 75 | def main(self): 76 | Echo("hi") 77 | File("/tmp/foo2.txt", from_file="files/foo2.txt", mode=0o770) 78 | Echo("that was easy") 79 | 80 | class Demo(Policy): 81 | 82 | def set_variables(self): 83 | return dict(asdf = 'jkl;') 84 | 85 | def set_roles(self): 86 | return Roles( 87 | WebServers(name='webservers', tag='webservers'), 88 | AnotherRole(tag='another') 89 | ) 90 | 91 | def allow_fileserving_patterns(self): 92 | # this is totally optional, the default is '*' 93 | return [ '*.txt', '*.j2' ] 94 | 95 | def deny_fileserving_patterns(self): 96 | # this is also optional, the defaults are already set 97 | return Policy.DEFAULT_DENY_FILESERVING_PATTERNS 98 | 99 | if __name__ == '__main__': 100 | inventory = TomlInventory("inventory/inventory.toml") 101 | Cli(Demo()) 102 | 103 | -------------------------------------------------------------------------------- /content/templates/foo.j2: -------------------------------------------------------------------------------- 1 | x={{ x }} and y={{ y }} and z={{ z }} 2 | 3 | -------------------------------------------------------------------------------- /content/user_facts.py: -------------------------------------------------------------------------------- 1 | # this is a demo of UserFacts which uses files baked into 2 | # /etc/opsmop/facts.d to implement feature flags 3 | 4 | from opsmop.core.easy import * 5 | import getpass 6 | USERNAME = getpass.getuser() 7 | 8 | class FeatureOne(Role): 9 | 10 | def should_process_when(self): 11 | return UserFacts.feature_flags.get('ff01', False) 12 | 13 | def main(self): 14 | 15 | command = T("Set warp speed to {{ UserFacts.warp_factor }}, Engage!") 16 | DebugFacts() 17 | Echo("The security officer is {{ UserFacts.crew.officers.security }}") 18 | Echo("The singularity is becoming unstable. {{ command }}") 19 | 20 | class FeatureTwo(Role): 21 | 22 | def main(self): 23 | 24 | if not UserFacts.feature_flags.get('ff02', False): 25 | return 26 | 27 | Echo("This won't actually run until you modify feature_flags.json to enable ff02") 28 | DebugFacts() 29 | 30 | class CommonSetup(Role): 31 | 32 | def main(self): 33 | f1 = Directory("/etc/opsmop/facts.d", owner=USERNAME) 34 | f2 = File("/etc/opsmop/facts.d/feature_flags.json", from_file="files/feature_flags.json", owner=USERNAME, mode=0o644) 35 | f3 = File("/etc/opsmop/facts.d/star_trek.yml", from_file="files/star_trek.yml", owner=USERNAME, mode=0o644) 36 | f4 = File("/etc/opsmop/facts.d/dynamic_facts.sh", from_file="files/dynamic_facts.sh", owner=USERNAME, mode=0o755) 37 | 38 | if f1.changed or f2.changed or f3.changed or f4.changed: 39 | Echo("fyi, we just set up /etc/opsmop/facts.d for you") 40 | Echo("check out the file contents and edit them if you like") 41 | 42 | UserFacts.invalidate() 43 | 44 | class Demo(Policy): 45 | 46 | def set_roles(self): 47 | return Roles( 48 | CommonSetup(), 49 | FeatureOne(), 50 | FeatureTwo() 51 | ) 52 | 53 | if __name__ == '__main__': 54 | Cli(Demo()) 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /content/var_scoping.py: -------------------------------------------------------------------------------- 1 | # 2 | # this is a contrived example designed to teach variable scoping behavior. 3 | # 4 | # usage: 5 | # 6 | # opsmop apply content/var_scoping.py 7 | # 8 | # ============================================================================== 9 | 10 | from opsmop.core.easy import * 11 | 12 | # ============================================================================== 13 | 14 | class One(Role): 15 | 16 | def set_variables(self): 17 | # here these are hard coded but they could also come from a database lookup 18 | # or from a config file 19 | return dict(some_unused_variable='...', level=1, foo=dict(x=1, y=2)) 20 | 21 | def main(self): 22 | car = "DeLorean" 23 | 24 | Echo("level={{ level }}, color={{ color }}, car={{ car }}, code={{ code }}, y={{ foo.y }} ") 25 | # direct variable access from Python works like this: 26 | # Echo("level = %s" % self.vars.level) 27 | # Echo("color = %s" % self.vars.color) 28 | 29 | # ============================================================================== 30 | 31 | class Two(Role): 32 | 33 | def set_variables(self): 34 | return dict(level='two', foo=dict(x=3,y=4)) 35 | 36 | def main(self): 37 | car = "NSX" 38 | code = 8675309 39 | Echo("level={{ level }}, color={{ color }}, car={{ car }}, code={{ code }}, y={{ foo.y }}") 40 | 41 | # ============================================================================== 42 | 43 | class ScopeTest(Policy): 44 | 45 | def set_variables(self): 46 | return dict(level=0, other=True) 47 | 48 | def set_roles(self): 49 | return Roles( 50 | One(color='red'), 51 | Two(color='green'), 52 | Two(color='blue') 53 | ) 54 | 55 | if __name__ == '__main__': 56 | Cli(ScopeTest(code=5150)) 57 | 58 | -------------------------------------------------------------------------------- /defaults.toml: -------------------------------------------------------------------------------- 1 | # the first file found will be read in: 2 | # 3 | # ~/.opsmop/defaults.toml 4 | # /etc/opsmop/defaults.toml 5 | # 6 | # these values can be overriden in policy files on a per-role basis. 7 | 8 | [ssh] 9 | default_username="opsmop" 10 | # default_password="opsmop" 11 | 12 | [sudo] 13 | default_username="root" 14 | # default_password="" 15 | 16 | -------------------------------------------------------------------------------- /module_docs/README.md: -------------------------------------------------------------------------------- 1 | Module Documentation 2 | ==================== 3 | 4 | The OpsMop module documentation, also used to generate content on [opsmop.io](http://opsmop.io) is readable and runnable out of GitHub! 5 | 6 | There are some additional demos in the '../content' directory. 7 | 8 | Usage 9 | ===== 10 | 11 | opsmop check module_docs/.py 12 | 13 | opsmop apply module_docs/.py 14 | 15 | 16 | -------------------------------------------------------------------------------- /module_docs/directory.py: -------------------------------------------------------------------------------- 1 | # MODULE: directory 2 | # PURPOSE: creates directories, removes them, and modifies metadata 3 | # CATEGORY: general 4 | # PROVIDERS: directory 5 | # RELATED: file 6 | # FYI: See the online documentation for the full parmameter list 7 | # 8 | # DESCRIPTION: 9 | # 10 | # The Directory module is very similar to the file module, but obviously 11 | # for directories, and not files. 12 | # ======================================================================================= 13 | 14 | from opsmop.core.easy import * 15 | import getpass 16 | USERNAME = getpass.getuser() 17 | 18 | # -------------------------------------------------------------------------------------- 19 | # EXAMPLE: Basic Example 20 | # 21 | # DESCRIPTION: 22 | # 23 | # This should be reasonably self explanatory... 24 | # ======================================================================================= 25 | 26 | class BasicExample(Role): 27 | 28 | def set_variables(self): 29 | return dict(a=1, b=5150, c="badwolf") 30 | 31 | def set_resources(self): 32 | return Resources( 33 | Directory(name="/tmp/opsmop-demo/sample_dir", absent=True), 34 | Directory(name="/tmp/opsmop-demo/sample_dir", owner=USERNAME, mode=0x755) 35 | ) 36 | 37 | 38 | # --------------------------------------------------------------------------------------- 39 | # SETUP: a helper role that sets up for this demo 40 | # ======================================================================================= 41 | 42 | class CommonSetup(Role): 43 | 44 | def set_resources(self): 45 | return Roles( 46 | ) 47 | 48 | # --------------------------------------------------------------------------------------- 49 | # POLICY: loads all of the above roles 50 | # ======================================================================================= 51 | 52 | class Demo(Policy): 53 | 54 | def set_roles(self): 55 | return Roles( 56 | CommonSetup(), 57 | BasicExample() 58 | ) 59 | 60 | if __name__ == '__main__': 61 | Cli(Demo()) 62 | 63 | 64 | -------------------------------------------------------------------------------- /module_docs/echo.py: -------------------------------------------------------------------------------- 1 | # MODULE: echo 2 | # PURPOSE: prints strings and status to the user 3 | # CATEGORY: special 4 | # PROVIDERS: echo 5 | # RELATED: debug, file 6 | # FYI: See the online documentation for the full parmameter list 7 | # 8 | # DESCRIPTION: 9 | # 10 | # The Echo module shows strings during the application of an OpsMop policy. 11 | # Unlike other modules, strings are automatically assumed to be templates. 12 | # ======================================================================================= 13 | 14 | from opsmop.core.easy import * 15 | import getpass 16 | USERNAME = getpass.getuser() 17 | 18 | # -------------------------------------------------------------------------------------- 19 | # EXAMPLE: Basic Example 20 | # 21 | # DESCRIPTION: 22 | # 23 | # If you install cowsay and export "MOO=1" this example will be more entertaining. 24 | # ======================================================================================= 25 | 26 | class BasicExample(Role): 27 | 28 | def set_variables(self): 29 | return dict(a=1, b=5150, c="badwolf") 30 | 31 | def main(self): 32 | b = 2 33 | Echo("The value of a is {{ a }} and b = {{ b }}") 34 | 35 | 36 | # --------------------------------------------------------------------------------------- 37 | # SETUP: a helper role that sets up for this demo 38 | # ======================================================================================= 39 | 40 | class CommonSetup(Role): 41 | 42 | def main(self): 43 | pass 44 | 45 | # --------------------------------------------------------------------------------------- 46 | # POLICY: loads all of the above roles 47 | # ======================================================================================= 48 | 49 | class Demo(Policy): 50 | 51 | def set_roles(self): 52 | return Roles( 53 | CommonSetup(), 54 | BasicExample(), 55 | ) 56 | 57 | if __name__ == '__main__': 58 | Cli(Demo()) 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /module_docs/file.py: -------------------------------------------------------------------------------- 1 | # MODULE: file 2 | # PURPOSE: copies files, changes modes, renders templates, deletes files, slices, dices 3 | # CATEGORY: general 4 | # PROVIDERS: file 5 | # RELATED: directory 6 | # FYI: See the online documentation for the full parmameter list 7 | # 8 | # DESCRIPTION: 9 | # 10 | # The File module handles all major types of file operations in OpsMop. 11 | # ======================================================================================= 12 | 13 | from opsmop.core.easy import * 14 | import getpass 15 | USERNAME = getpass.getuser() 16 | 17 | # -------------------------------------------------------------------------------------- 18 | # EXAMPLE: Template 19 | # SEE_FILE: templates/foo.txt.j2 20 | # 21 | # DESCRIPTION: 22 | # 23 | # Templating a file from a jinja2 template 24 | # 25 | # See the official `Jinja2 documentation `_ for full capabilities 26 | # of Jinja2 templates 27 | # ======================================================================================= 28 | 29 | class Jinja2TemplateExample(Role): 30 | 31 | def set_variables(self): 32 | return dict(a=1, b=5150, c="badwolf") 33 | 34 | def main(self): 35 | # for template language and variable scoping information, please consult the language docs 36 | File(name="/tmp/opsmop-demo/foo1.txt", from_template="templates/foo.txt.j2") 37 | Shell("cat /tmp/opsmop-demo/foo1.txt") 38 | 39 | 40 | 41 | # -------------------------------------------------------------------------------------- 42 | # EXAMPLE: Copy 43 | # 44 | # DESCRIPTION: 45 | # 46 | # Copying a file with owner, permission, and mode 47 | # ======================================================================================= 48 | 49 | class CopyExample(Role): 50 | 51 | def main(self): 52 | 53 | # owner/group/mode can be used with any of these forms, just showing one example here 54 | File(name="/tmp/opsmop-demo/foo2.txt", from_file="files/foo.txt", owner=USERNAME, mode=0x755) 55 | Shell("cat /tmp/opsmop-demo/foo2.txt") 56 | 57 | # -------------------------------------------------------------------------------------- 58 | # EXAMPLE: Copy From String 59 | # 60 | # DESCRIPTION: 61 | # 62 | # For very small files, this is also possible 63 | # ======================================================================================= 64 | 65 | class ContentExample(Role): 66 | 67 | def set_variables(self): 68 | return dict(a=2, b=2112, c="darmok") 69 | 70 | def main(self): 71 | 72 | File(name="/tmp/opsmop-demo/foo3.txt", from_content="Happy Birthday") 73 | Shell("cat /tmp/opsmop-demo/foo3.txt") 74 | File(name="/tmp/opsmop-demo/foo4.txt", from_content=T("Template test! a={{ a}}")) 75 | Shell("cat /tmp/opsmop-demo/foo4.txt") 76 | 77 | # --------------------------------------------------------------------------------------- 78 | # EXAMPLE: Downloading a File 79 | # 80 | # DESCRIPTION: 81 | # 82 | # Grabbing a file from a web location 83 | # ======================================================================================= 84 | 85 | class FromUrlExample(Role): 86 | 87 | 88 | def main(self): 89 | 90 | src = "https://raw.githubusercontent.com/opsmop/opsmop/master/README.md" 91 | File(name="/tmp/opsmop-demo/foo5.txt", from_url=src, overwrite=False) 92 | 93 | # --------------------------------------------------------------------------------------- 94 | # EXAMPLE: Deleting a File 95 | # 96 | # DESCRIPTION: 97 | # 98 | # Ensure that a file does not exist 99 | # ======================================================================================= 100 | 101 | class AbsentExample(Role): 102 | 103 | def main(self): 104 | File(name="/tmp/opsmop-demo/foo4.txt", absent=True) 105 | Shell("ls /tmp/opsmop-demo/foo4.txt", ignore_errors=True) 106 | 107 | # --------------------------------------------------------------------------------------- 108 | # SETUP: a helper role that sets up for this demo 109 | # ======================================================================================= 110 | 111 | class CommonSetup(Role): 112 | 113 | def main(self): 114 | Directory(name="/tmp/opsmop-demo/") 115 | 116 | # --------------------------------------------------------------------------------------- 117 | # POLICY: loads all of the above roles 118 | # ======================================================================================= 119 | 120 | class Demo(Policy): 121 | 122 | def set_roles(self): 123 | return Roles( 124 | CommonSetup(), 125 | Jinja2TemplateExample(d=4, e=5, f=6), 126 | CopyExample(), 127 | ContentExample(), 128 | FromUrlExample(), 129 | AbsentExample() 130 | ) 131 | 132 | if __name__ == '__main__': 133 | Cli(Demo()) 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /module_docs/files/foo.txt: -------------------------------------------------------------------------------- 1 | hello world 2 | -------------------------------------------------------------------------------- /module_docs/group.py: -------------------------------------------------------------------------------- 1 | # MODULE: groups 2 | # PURPOSE: manages user groups 3 | # CATEGORY: general 4 | # PROVIDERS: group.groupadd 5 | # RELATED: user 6 | # FYI: See the online documentation for the full parmameter list 7 | # 8 | # DESCRIPTION: 9 | # 10 | # Manages groups. To control what users are in what groups, see :ref:`module_user`. 11 | # ======================================================================================= 12 | 13 | from opsmop.core.easy import * 14 | 15 | # -------------------------------------------------------------------------------------- 16 | # EXAMPLE: Basic Example 17 | # 18 | # DESCRIPTION: 19 | # 20 | # Basic group operations. 21 | # ======================================================================================= 22 | 23 | class BasicExample(Role): 24 | 25 | def set_variables(self): 26 | return dict(a=1, b=5150, c="badwolf") 27 | 28 | def main(self): 29 | Group(name="opsmgrp1") 30 | Group(name="opsmgrp2", gid=8008) 31 | Group(name="opsmgrp3", system=True) 32 | Group(name="opsmgrp1", absent=True) 33 | 34 | 35 | 36 | # --------------------------------------------------------------------------------------- 37 | # SETUP: a helper role that sets up for this demo 38 | # ======================================================================================= 39 | 40 | class CommonSetup(Role): 41 | 42 | def main(self): 43 | pass 44 | 45 | # --------------------------------------------------------------------------------------- 46 | # POLICY: loads all of the above roles 47 | # ======================================================================================= 48 | 49 | class Demo(Policy): 50 | 51 | def set_roles(self): 52 | return Roles( 53 | CommonSetup(), 54 | BasicExample(), 55 | ) 56 | 57 | if __name__ == '__main__': 58 | Cli(Demo()) 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /module_docs/package.py: -------------------------------------------------------------------------------- 1 | # MODULE: package 2 | # PURPOSE: installs, removes, and upgrades packages 3 | # CATEGORY: general 4 | # PROVIDERS: package.apt, package.brew, package.yum, package.dnf 5 | # RELATED: file, service 6 | # FYI: See the online documentation for the full parmameter list 7 | # 8 | # DESCRIPTION: 9 | # 10 | # The package module is used to install packages. The default provider for the operating 11 | # system (for example, 'apt') is used unless the parameter 'method' is supplied to 12 | # pick an alternate provider. See :ref:`method`. 13 | # ======================================================================================= 14 | 15 | from opsmop.core.easy import * 16 | import getpass 17 | USERNAME = getpass.getuser() 18 | 19 | # -------------------------------------------------------------------------------------- 20 | # EXAMPLE: Basic Example 21 | # 22 | # DESCRIPTION: 23 | # 24 | # Installing and removing a package. 25 | # ======================================================================================= 26 | 27 | class BasicExample(Role): 28 | 29 | def set_variables(self): 30 | return dict(a=1, b=5150, c="badwolf") 31 | 32 | def main(self): 33 | 34 | # explicit package provider selection works like this 35 | # Package(name='cowsay', method='yum') 36 | 37 | # uninstall 38 | Package(name='cowsay', absent=True) 39 | 40 | # install 41 | Package(name='cowsay') 42 | 43 | 44 | 45 | 46 | # --------------------------------------------------------------------------------------- 47 | # SETUP: a helper role that sets up for this demo 48 | # ======================================================================================= 49 | 50 | class CommonSetup(Role): 51 | 52 | def main(self): 53 | pass 54 | 55 | # --------------------------------------------------------------------------------------- 56 | # POLICY: loads all of the above roles 57 | # ======================================================================================= 58 | 59 | class Demo(Policy): 60 | 61 | def set_roles(self): 62 | return Roles( 63 | CommonSetup(), 64 | BasicExample(), 65 | ) 66 | 67 | if __name__ == '__main__': 68 | Cli(Demo()) 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /module_docs/service.py: -------------------------------------------------------------------------------- 1 | # MODULE: service 2 | # PURPOSE: starts/stops and enables/disables services 3 | # CATEGORY: general 4 | # PROVIDERS: providers.brew, providers.systemd 5 | # RELATED: file, package 6 | # FYI: See the online documentation for the full parmameter list 7 | # 8 | # DESCRIPTION: 9 | # 10 | # The Service module starts, stops, and enables or disables services. The default provider 11 | # for the operating system (for example, 'apt') is used unless the parameter 'method' is 12 | # supplied to pick an alternate provider. See :ref:`method`. 13 | # ======================================================================================= 14 | 15 | from opsmop.core.easy import * 16 | import getpass 17 | USERNAME = getpass.getuser() 18 | 19 | # -------------------------------------------------------------------------------------- 20 | # EXAMPLE: Basic Example 21 | # 22 | # DESCRIPTION: 23 | # 24 | # Various service operations. Change the name to a service you don't mind 25 | # restarting. 26 | # ======================================================================================= 27 | 28 | class BasicExample(Role): 29 | 30 | def main(self): 31 | 32 | Service(name='postgres', started=True, enabled=True) 33 | Service(name='postgres', started=False, enabled=False) 34 | Service(name='postgres', started=True, enabled=True) 35 | 36 | 37 | # --------------------------------------------------------------------------------------- 38 | # SETUP: a helper role that sets up for this demo 39 | # ======================================================================================= 40 | 41 | class CommonSetup(Role): 42 | 43 | def main(self): 44 | pass 45 | 46 | # --------------------------------------------------------------------------------------- 47 | # POLICY: loads all of the above roles 48 | # ======================================================================================= 49 | 50 | class Demo(Policy): 51 | 52 | def set_roles(self): 53 | return Roles( 54 | CommonSetup(), 55 | BasicExample(), 56 | ) 57 | 58 | if __name__ == '__main__': 59 | Cli(Demo()) 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /module_docs/shell.py: -------------------------------------------------------------------------------- 1 | # MODULE: shell 2 | # PURPOSE: runs arbitrary shell commands, with feeling 3 | # CATEGORY: general 4 | # PROVIDERS: shell 5 | # RELATED: file, package, service, set 6 | # FYI: See the online documentation for the full parmameter list 7 | # 8 | # DESCRIPTION: 9 | # 10 | # The Shell module runs arbitrary shell commands. It is frequently used with template 11 | # expressions "T()" to pass variables in for execution. 12 | # ======================================================================================= 13 | 14 | from opsmop.core.easy import * 15 | import getpass 16 | 17 | # -------------------------------------------------------------------------------------- 18 | # EXAMPLE: Basic Example 19 | # 20 | # DESCRIPTION: 21 | # 22 | # Here are various passing asserts and finally one that will end the execution 23 | # of this policy with an error. 24 | # ======================================================================================= 25 | 26 | class BasicExample(Role): 27 | 28 | def set_variables(self): 29 | return dict(a=1, b=5150, c="badwolf") 30 | 31 | def main(self): 32 | 33 | # here is an example of running a command and saving the output and return code 34 | date = Shell("date | cut -f1 -d ' '") 35 | Echo("today is {{ date.data }} and the return code was {{ date.rc }}") 36 | 37 | # you can ignore return codes like this 38 | Shell("/bin/false", ignore_errors=True), 39 | 40 | # or like this - soon 41 | Shell("/bin/false", failed_when=lambda x: x.rc != 42) 42 | 43 | # you can use variables in shell commands like this: 44 | Shell(T("echo {{ a }} {{ b }} {{ c }}")) 45 | 46 | # --------------------------------------------------------------------------------------- 47 | # SETUP: a helper role that sets up for this demo 48 | # ======================================================================================= 49 | 50 | class CommonSetup(Role): 51 | 52 | def main(self): 53 | pass 54 | 55 | # --------------------------------------------------------------------------------------- 56 | # POLICY: loads all of the above roles 57 | # ======================================================================================= 58 | 59 | class Demo(Policy): 60 | 61 | def set_roles(self): 62 | return Roles( 63 | CommonSetup(), 64 | BasicExample(), 65 | ) 66 | 67 | if __name__ == '__main__': 68 | Cli(Demo()) 69 | -------------------------------------------------------------------------------- /module_docs/templates/foo.txt.j2: -------------------------------------------------------------------------------- 1 | # Jinja2 Templates are most frequently used to generate configuration files. 2 | # Here is a basic one. 3 | 4 | a={{ a }} 5 | b={{ b }} 6 | 7 | # See the official Jinja2 documentation for information about conditionals, loops, and 8 | # more inside of templates. 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /module_docs/user.py: -------------------------------------------------------------------------------- 1 | # MODULE: user 2 | # PURPOSE: manages user accounts 3 | # CATEGORY: general 4 | # PROVIDERS: user.useradd 5 | # RELATED: group 6 | # FYI: See the online documentation for the full parmameter list 7 | # 8 | # DESCRIPTION: 9 | # 10 | # The user module manages user accounts. Certain parameters can only be set at creation time. 11 | # Also see the group module for additional capabilities. 12 | # ======================================================================================= 13 | 14 | from opsmop.core.easy import * 15 | 16 | # -------------------------------------------------------------------------------------- 17 | # EXAMPLE: Basic Example 18 | # 19 | # DESCRIPTION: 20 | # 21 | # Creating and removing some users 22 | # ======================================================================================= 23 | 24 | class BasicExample(Role): 25 | 26 | def set_variables(self): 27 | return dict(a=1, b=5150, c="badwolf") 28 | 29 | def set_resources(self): 30 | return Resources( 31 | User(name="opsmguest1"), 32 | User(name="opsmguest2", uid=3003, group="sudo", groups=['vespene'], shell="/bin/bash"), 33 | User(name="opsmguest3", system=True), 34 | User(name="opsmguest1", absent=True) 35 | ) 36 | 37 | 38 | 39 | # --------------------------------------------------------------------------------------- 40 | # SETUP: a helper role that sets up for this demo 41 | # ======================================================================================= 42 | 43 | class CommonSetup(Role): 44 | 45 | def set_resources(self): 46 | return Roles( 47 | ) 48 | 49 | # --------------------------------------------------------------------------------------- 50 | # POLICY: loads all of the above roles 51 | # ======================================================================================= 52 | 53 | class Demo(Policy): 54 | 55 | def set_roles(self): 56 | return Roles( 57 | CommonSetup(), 58 | BasicExample(), 59 | ) 60 | 61 | if __name__ == '__main__': 62 | Cli(Demo()) 63 | 64 | 65 | 66 | 67 | 68 | --------------------------------------------------------------------------------