Fuente: Ansible Blog
Fuente: sysadmincasts
[web]
10.0.15.21
10.0.15.22
[db]
10.0.15.23
[db:vars]
db_user=guest
[infrastructure:children]
web
db
ansible grupo -m módulo -a opciones
(ansible)alex ~ $ ansible all -m ping
10.0.15.22 | success >> {
"changed": false,
"ping": "pong"
}
10.0.15.21 | success >> {
"changed": false,
"ping": "pong"
}
10.0.15.23 | success >> {
"changed": false,
"ping": "pong"
}
ansible all -m ping
- name: Ensure that nginx is started
service: name=nginx state=started
---
- hosts: web
sudo: yes
vars:
- external_port: 80
- internal_port: 8000
tasks:
- name: Add the apt repository for nginx
apt_repository: repo="ppa:nginx/stable" update_cache=yes
- name: Install nginx
apt: name=nginx state=present
- name: Ensure that nginx is started
service: name=nginx state=started
- name: Remove default site
file: path=/etc/nginx/sites-enabled/default state=absent
notify:
- Restart nginx
- name: Configure a site
template: src=templates/site.j2 dest=/etc/nginx/sites-available/site
- name: Enable a site
file: src=/etc/nginx/sites-available/site dest=/etc/nginx/sites-enabled/site state=link
notify:
- Restart nginx
handlers:
- name: Restart nginx
service: name=nginx state=restarted
intro/deploy-nginx.yml
$ ansible-playbook deploy-nginx.yml
PLAY [web] ********************************************************************
GATHERING FACTS ***************************************************************
ok: [10.0.15.21]
ok: [10.0.15.22]
TASK: [Add the apt repository for nginx] **************************************
changed: [10.0.15.21]
changed: [10.0.15.22]
TASK: [Install nginx] *********************************************************
changed: [10.0.15.22]
changed: [10.0.15.21]
TASK: [Ensure that nginx is started] ******************************************
ok: [10.0.15.21]
ok: [10.0.15.22]
TASK: [Remove default site] ***************************************************
changed: [10.0.15.21]
changed: [10.0.15.22]
TASK: [Configure a site] ******************************************************
changed: [10.0.15.21]
changed: [10.0.15.22]
TASK: [Enable a site] *********************************************************
changed: [10.0.15.21]
changed: [10.0.15.22]
TASK: [Remove default site] ***************************************************
ok: [10.0.15.21]
ok: [10.0.15.22]
NOTIFIED: [Restart nginx] *****************************************************
changed: [10.0.15.21]
changed: [10.0.15.22]
PLAY RECAP ********************************************************************
10.0.15.21 : ok=9 changed=6 unreachable=0 failed=0
10.0.15.22 : ok=9 changed=6 unreachable=0 failed=0
Los roles encapsulan:
from ansible.runner import Runner
from ansible.inventory import Inventory
inventory = Inventory(["localhost"])
runner = Runner(module_name='setup', module_args='',
pattern='localhost', inventory=inventory,
transport="local")
result = runner.run()
ansible all -m setup
Obtienes:
from ansible.runner import Runner
from ansible.inventory import Inventory
from flask import Flask
from flask_restful import Resource, Api
class AnsibleFactResource(Resource):
"""
A read-only local ansible fact
"""
def __init__(self):
self.facts = None
Resource.__init__(self)
def get_local_facts(self):
"""
Loads the Ansible local facts in self.facts
Calls the Ansible Python API v1 'setup' module
"""
inventory = Inventory(["localhost"])
runner = Runner(module_name='setup', module_args='',
pattern='localhost', inventory=inventory,
transport="local")
result = runner.run()
self.facts = result["contacted"]["localhost"]["ansible_facts"]
def get(self, fact_name):
"""
Returns a top-level fact (not nested)
"""
self.get_local_facts()
try:
return self.facts[fact_name]
except KeyError:
return "Not a valid fact: %s" % fact_name
app = Flask(__name__)
api = Api(app)
api.add_resource(AnsibleFactResource, '/facts/<string:fact_name>')
if __name__ == '__main__':
app.run(debug=True, port=8000)
embedding/task/flask_facter.py
from ansible.playbook import PlayBook
from ansible.inventory import Host, Inventory
from ansible import callbacks
from ansible import utils
# Create the inventory
controller = Host(name = "localhost")
controller.set_variable('users', user_list)
controller.set_variable('apt_packages', package_list)
local_inventory = Inventory([])
local_inventory.get_group('all').add_host(controller)
# Boilerplate for callbacks setup
utils.VERBOSITY = 0
# Output callbacks setup
output_callbacks = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
# API callbacks setup
stats = callbacks.AggregateStats()
api_callbacks = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
provision_playbook = PlayBook(playbook = "installer.yml",
stats = stats,
callbacks = output_callbacks,
runner_callbacks = api_callbacks,
inventory = local_inventory,
transport = "local",
become_pass = sudo_password
)
playbook_result = provision_playbook.run()
import sys
from getpass import getpass
from ansible.playbook import PlayBook
from ansible.inventory import Host, Inventory
from ansible import callbacks
from ansible import utils
def run_installer(user_list, package_list, sudo_password):
"""
Runs the playbook `installer.yml` with the supplied parameters
"""
# Create the inventory
controller = Host(name = "localhost")
controller.set_variable('users', user_list)
controller.set_variable('apt_packages', package_list)
local_inventory = Inventory([])
local_inventory.get_group('all').add_host(controller)
# Boilerplate for callbacks setup
utils.VERBOSITY = 0
# Output callbacks setup
output_callbacks = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
# API callbacks setup
stats = callbacks.AggregateStats()
api_callbacks = callbacks.PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
provision_playbook = PlayBook(playbook = "installer.yml",
stats = stats,
callbacks = output_callbacks,
runner_callbacks = api_callbacks,
inventory = local_inventory,
transport = "local",
become_pass = sudo_password
)
playbook_result = provision_playbook.run()
return playbook_result
def get_selection_list(initial_prompt, input_prompt, continue_prompt):
"""
Return a selection list chosen according to a flow described by:
- initial_prompt: To enter the selection menu
- input_prompt: To enter an item
- continue_prompt: To continue entering another item
"""
results = []
enter_selection = raw_input(initial_prompt)
enter_selection = True if enter_selection in ["y", "Y", "yes"] else False
if enter_selection:
while True:
current_result = raw_input(input_prompt)
if not current_result:
break
results.append(current_result)
continue_selection = raw_input(continue_prompt)
continue_selection = True if continue_selection in ["y","Y","yes"] else False
if not continue_selection:
break
return results
if __name__ == '__main__':
packages = []
print("")
print("Automated installation script")
print("=============================")
print("")
sudo_password = getpass("Enter sudo password:")
users = get_selection_list(
"Create users? (y/N)",
"Enter username:",
"Add more users? (y/N)"
)
print("")
packages = get_selection_list(
"Install packages? (y/N)",
"Enter package name:",
"Add more packages? (y/N)"
)
run_installer(user_list=users, package_list=packages, sudo_password=sudo_password)
embedding/playbook/installer.py
Fuente: Ansible Blog
#!/usr/bin/python
# -*- coding: utf-8 -*-
DOCUMENTATION = '''
'''
EXAMPLES = '''
'''
# Your custom Python code / external libraries
from mypackage import mypythonicfunction
def mypythonicfunction(...):
"""
Here you do the hard work. Use external libraries or your own code
And return a meaningful value and message to main()
"""
return status, msg
def main():
module = AnsibleModule(
argument_spec=dict(
# Here you parse the arguments
)
# and set the options and behaviour of Ansible
)
# Here you assign your arguments to Python variables
variable_a = module.params['var_a']
# and call your mypythonicfunction() with the variables
status, msg = mypythonicfunction(variable_a, ...)
# Parse the results and return valid JSONs and meaningful messages
if status == "ok":
module.exit_json(changed=changed, msg=msg)
else:
module.fail_json(msg=msg)
# Boilerplate but IMPORTANT code
from ansible.module_utils.basic import *
if __name__ == "__main__":
main()
Puntos clave:
exit_json(changed, ...) fail_json(msg, ...)
from ansible.module_utils.basic import *
if __name__ == "__main__":
main()
ansible/hacking/test-module
-m /path/to/module
-a module_args
extending/module/library/taiga_issue.py
#!/usr/bin/env python
import argparse
import json
import sys
import shelve
import ConfigParser
def parse_args():
parser = argparse.ArgumentParser(description="Shelve inventory script")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--list', action='store_true')
group.add_argument('--host')
return parser.parse_args()
def get_shelve_groups(shelvefile):
d = shelve.open(shelvefile)
g = d["groups"]
d.close()
return g
def get_shelve_host_details(shelvefile, host):
d = shelve.open(shelvefile)
h = d["hostvars"][host]
d.close()
return h
def main():
config = ConfigParser.RawConfigParser()
config.read('shelve_inventory.ini')
shelvefile = config.get('defaults', 'shelvefile')
args = parse_args()
if args.list:
groups = get_shelve_groups(shelvefile)
json.dump(groups, sys.stdout)
else:
details = get_shelve_host_details(shelvefile, args.host)
json.dump(details, sys.stdout)
if __name__ == '__main__':
main()
extending/dynamic_inventory/shelve_inventory.py
[defaults]
callback_plugins = ./plugins
action_plugins/
lookup_plugins/
callback_plugins/
connection_plugins/
filter_plugins/
vars_plugins/
from subprocess import call
def notify_send(title, message, icon):
call(["notify-send", title, message,"-i", icon])
class CallbackModule(object):
def runner_on_failed(self, host, res, ignore_errors=False):
template = "Host: {}\nModule: {}\nMessage: {}"
notification = template.format(
host,
res.get('invocation').get('module_name'),
res.get('msg')
)
notify_send("ANSIBLE FAILURE", notification, "dialog-warning")
extending/plugin/callbacks/plugins/notify_send.py
class Connection(object):
def __init__(self, runner, host, port, user,
password, private_key_file, *args, **kwargs):
[...]
def connect(self):
[...]
def exec_command(self, cmd, tmp_path, become_user=None,
sudoable=False, executable='/bin/sh', in_data=None):
[...]
def put_file(self, in_path, out_path):
[...]
def fetch_file(self, in_path, out_path):
[...]
def close(self):
[...]
contents: "{{ lookup('file', '/etc/foo.txt') }}"
from ansible import utils, errors
class LookupModule(object):
def __init__(self, basedir=None, **kwargs):
self.basedir = basedir
def run(self, terms, inject=None, **kwargs):
# terms holds the parameters of the lookup call
import shelve
import os
from ansible import utils, errors
class LookupModule(object):
def __init__(self, basedir=None, **kwargs):
self.basedir = basedir
def read_shelve(self, shelve_filename, key):
"""
Read the value of "key" from a shelve file
"""
d = shelve.open(shelve_filename)
res = d.get(key, None)
d.close()
return res
def run(self, terms, inject=None, **kwargs):
terms = utils.listify_lookup_plugin_terms(terms, self.basedir, inject)
ret = []
if not isinstance(terms, list):
terms = [ terms ]
for term in terms:
playbook_path = None
relative_path = None
paramvals = {"file": None, "key": None}
params = term.split()
try:
for param in params:
name, value = param.split('=')
assert(name in paramvals)
paramvals[name] = value
except (ValueError, AssertionError), e:
# In case "file" or "key" are not present
raise errors.AnsibleError(e)
file = paramvals['file']
key = paramvals['key']
basedir_path = utils.path_dwim(self.basedir, file)
# Search also in the role/files directory and in the playbook directory
if '_original_file' in inject:
relative_path = utils.path_dwim_relative(inject['_original_file'], 'files', file, self.basedir, check=False)
if 'playbook_dir' in inject:
playbook_path = os.path.join(inject['playbook_dir'], file)
for path in (basedir_path, relative_path, playbook_path):
if path and os.path.exists(path):
res = self.read_shelve(path, key)
if res is None:
raise errors.AnsibleError("Key %s not found in shelve file %s" % (key, file))
# Convert the value read to string
ret.append(str(res))
break
else:
raise errors.AnsibleError("Could not locate shelve file in lookup: %s" % file)
return ret
extending/plugin/lookup/plugins/shelvefile.py
Uso:
{{ lookup('shelvefile', 'file=book.db key=current_book_author') }}
{{ some_variable | default(5) }}
{{ some_list | min }}
{{ some_register_variable | changed }}
class FilterModule(object):
def filters(self):
return {
'filter1': filter1functionname,
'filter2': filter2functionname,
[...]
}
def rot13(s):
def lookup(v):
o, c = ord(v), v.lower()
if 'a' <= c <= 'm':
return chr(o + 13)
if 'n' <= c <= 'z':
return chr(o - 13)
return v
return ''.join(map(lookup, s))
class FilterModule(object):
def filters(self):
return {
'rot13': rot13
}
extending/plugin/filter/plugins/rot13.py
Uso:
{{ variable | rot13 }}
class VarsModule(object):
"""
Loads variables for groups and/or hosts
"""
def __init__(self, inventory):
""" constructor """
self.inventory = inventory
self.inventory_basedir = inventory.basedir()
def run(self, host, vault_password=None):
""" For backwards compatibility, when only vars per host were retrieved
This method should return both host specific vars as well as vars
calculated from groups it is a member of """
return {}
def get_host_vars(self, host, vault_password=None):
""" Get host specific variables. """
return {}
def get_group_vars(self, group, vault_password=None):
""" Get group specific variables. """
return {}
Fact caching:
hostvars de hosts no contactados en la ejecución del playbook
v1.8+
Para usarlo, pon esto en tu ansible.cfg:
[defaults]
gathering = smart
fact_caching = redis # Name of the plugin
fact_caching_timeout = 86400
import exceptions
class BaseCacheModule(object):
def get(self, key):
raise exceptions.NotImplementedError
def set(self, key, value):
raise exceptions.NotImplementedError
def keys(self):
raise exceptions.NotImplementedError
def contains(self, key):
raise exceptions.NotImplementedError
def delete(self, key):
raise exceptions.NotImplementedError
def flush(self):
raise exceptions.NotImplementedError
def copy(self):
raise exceptions.NotImplementedError
Libros:
Artículos:
Happy hacking!