1
# Copyright 2013 Canonical Ltd.
4
# Charm Helpers Developers <juju@lists.ubuntu.com>
5
"""Charm Helpers ansible - declare the state of your machines.
7
This helper enables you to declare your machine state, rather than
8
program it procedurally (and have to test each change to your procedures).
9
Your install hook can be as simple as:
12
import charmhelpers.contrib.ansible
16
charmhelpers.contrib.ansible.install_ansible_support()
17
charmhelpers.contrib.ansible.apply_playbook('playbooks/install.yaml')
20
and won't need to change (nor will its tests) when you change the machine
23
All of your juju config and relation-data are available as template
24
variables within your playbooks and templates. An install playbook looks
33
- name: Add private repositories.
35
src: ../templates/private-repositories.list.jinja2
36
dest: /etc/apt/sources.list.d/private.list
38
- name: Update the cache.
41
- name: Install dependencies.
49
group: name={{ item.name }} gid={{ item.gid }}
51
- { name: 'deploy_user', gid: 1800 }
52
- { name: 'service_user', gid: 1500 }
57
Read more online about playbooks[1] and standard ansible modules[2].
59
[1] http://www.ansibleworks.com/docs/playbooks.html
60
[2] http://www.ansibleworks.com/docs/modules.html
65
import charmhelpers.contrib.templating.contexts
66
import charmhelpers.core.host
67
import charmhelpers.core.hookenv
68
import charmhelpers.fetch
71
charm_dir = os.environ.get('CHARM_DIR', '')
72
ansible_hosts_path = '/etc/ansible/hosts'
73
# Ansible will automatically include any vars in the following
74
# file in its inventory when run locally.
75
ansible_vars_path = '/etc/ansible/host_vars/localhost'
78
def install_ansible_support(from_ppa=True):
79
"""Installs the ansible package.
81
By default it is installed from the PPA [1] linked from
82
the ansible website [2].
84
[1] https://launchpad.net/~rquillo/+archive/ansible
85
[2] http://www.ansibleworks.com/docs/gettingstarted.html#ubuntu-and-debian
87
If from_ppa is false, you must ensure that the package is available
88
from a configured repository.
91
charmhelpers.fetch.add_source('ppa:rquillo/ansible')
92
charmhelpers.fetch.apt_update(fatal=True)
93
charmhelpers.fetch.apt_install('ansible')
94
with open(ansible_hosts_path, 'w+') as hosts_file:
95
hosts_file.write('localhost ansible_connection=local')
98
def apply_playbook(playbook, tags=None):
100
tags = ",".join(tags)
101
charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
102
ansible_vars_path, namespace_separator='__',
103
allow_hyphens_in_keys=False)
111
call.extend(['--tags', '{}'.format(tags)])
112
subprocess.check_call(call)
115
class AnsibleHooks(charmhelpers.core.hookenv.Hooks):
116
"""Run a playbook with the hook-name as the tag.
118
This helper builds on the standard hookenv.Hooks helper,
119
but additionally runs the playbook with the hook-name specified
120
using --tags (ie. running all the tasks tagged with the hook-name).
123
hooks = AnsibleHooks(playbook_path='playbooks/my_machine_state.yaml')
125
# All the tasks within my_machine_state.yaml tagged with 'install'
126
# will be run automatically after do_custom_work()
131
# For most of your hooks, you won't need to do anything other
132
# than run the tagged tasks for the hook:
133
@hooks.hook('config-changed', 'start', 'stop')
134
def just_use_playbook():
137
# As a convenience, you can avoid the above noop function by specifying
138
# the hooks which are handled by ansible-only and they'll be registered
140
# hooks = AnsibleHooks(
141
# 'playbooks/my_machine_state.yaml',
142
# default_hooks=['config-changed', 'start', 'stop'])
144
if __name__ == "__main__":
145
# execute a hook based on the name the program is called by
146
hooks.execute(sys.argv)
149
def __init__(self, playbook_path, default_hooks=None):
150
"""Register any hooks handled by ansible."""
151
super(AnsibleHooks, self).__init__()
153
self.playbook_path = playbook_path
155
default_hooks = default_hooks or []
156
noop = lambda *args, **kwargs: None
157
for hook in default_hooks:
158
self.register(hook, noop)
160
def execute(self, args):
161
"""Execute the hook followed by the playbook using the hook as tag."""
162
super(AnsibleHooks, self).execute(args)
163
hook_name = os.path.basename(args[0])
164
charmhelpers.contrib.ansible.apply_playbook(
165
self.playbook_path, tags=[hook_name])