~brad-marshall/charms/trusty/apache2-wsgi/fix-haproxy-relations

« back to all changes in this revision

Viewing changes to hooks/lib/charmhelpers/contrib/ansible/__init__.py

  • Committer: Robin Winslow
  • Date: 2014-05-27 14:00:44 UTC
  • Revision ID: robin.winslow@canonical.com-20140527140044-8rpmb3wx4djzwa83
Add all files

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2013 Canonical Ltd.
 
2
#
 
3
# Authors:
 
4
#  Charm Helpers Developers <juju@lists.ubuntu.com>
 
5
"""Charm Helpers ansible - declare the state of your machines.
 
6
 
 
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:
 
10
 
 
11
{{{
 
12
import charmhelpers.contrib.ansible
 
13
 
 
14
 
 
15
def install():
 
16
    charmhelpers.contrib.ansible.install_ansible_support()
 
17
    charmhelpers.contrib.ansible.apply_playbook('playbooks/install.yaml')
 
18
}}}
 
19
 
 
20
and won't need to change (nor will its tests) when you change the machine
 
21
state.
 
22
 
 
23
All of your juju config and relation-data are available as template
 
24
variables within your playbooks and templates. An install playbook looks
 
25
something like:
 
26
 
 
27
{{{
 
28
---
 
29
- hosts: localhost
 
30
  user: root
 
31
 
 
32
  tasks:
 
33
    - name: Add private repositories.
 
34
      template:
 
35
        src: ../templates/private-repositories.list.jinja2
 
36
        dest: /etc/apt/sources.list.d/private.list
 
37
 
 
38
    - name: Update the cache.
 
39
      apt: update_cache=yes
 
40
 
 
41
    - name: Install dependencies.
 
42
      apt: pkg={{ item }}
 
43
      with_items:
 
44
        - python-mimeparse
 
45
        - python-webob
 
46
        - sunburnt
 
47
 
 
48
    - name: Setup groups.
 
49
      group: name={{ item.name }} gid={{ item.gid }}
 
50
      with_items:
 
51
        - { name: 'deploy_user', gid: 1800 }
 
52
        - { name: 'service_user', gid: 1500 }
 
53
 
 
54
  ...
 
55
}}}
 
56
 
 
57
Read more online about playbooks[1] and standard ansible modules[2].
 
58
 
 
59
[1] http://www.ansibleworks.com/docs/playbooks.html
 
60
[2] http://www.ansibleworks.com/docs/modules.html
 
61
"""
 
62
import os
 
63
import subprocess
 
64
 
 
65
import charmhelpers.contrib.templating.contexts
 
66
import charmhelpers.core.host
 
67
import charmhelpers.core.hookenv
 
68
import charmhelpers.fetch
 
69
 
 
70
 
 
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'
 
76
 
 
77
 
 
78
def install_ansible_support(from_ppa=True):
 
79
    """Installs the ansible package.
 
80
 
 
81
    By default it is installed from the PPA [1] linked from
 
82
    the ansible website [2].
 
83
 
 
84
    [1] https://launchpad.net/~rquillo/+archive/ansible
 
85
    [2] http://www.ansibleworks.com/docs/gettingstarted.html#ubuntu-and-debian
 
86
 
 
87
    If from_ppa is false, you must ensure that the package is available
 
88
    from a configured repository.
 
89
    """
 
90
    if from_ppa:
 
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')
 
96
 
 
97
 
 
98
def apply_playbook(playbook, tags=None):
 
99
    tags = tags or []
 
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)
 
104
    call = [
 
105
        'ansible-playbook',
 
106
        '-c',
 
107
        'local',
 
108
        playbook,
 
109
    ]
 
110
    if tags:
 
111
        call.extend(['--tags', '{}'.format(tags)])
 
112
    subprocess.check_call(call)
 
113
 
 
114
 
 
115
class AnsibleHooks(charmhelpers.core.hookenv.Hooks):
 
116
    """Run a playbook with the hook-name as the tag.
 
117
 
 
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).
 
121
 
 
122
    Example:
 
123
        hooks = AnsibleHooks(playbook_path='playbooks/my_machine_state.yaml')
 
124
 
 
125
        # All the tasks within my_machine_state.yaml tagged with 'install'
 
126
        # will be run automatically after do_custom_work()
 
127
        @hooks.hook()
 
128
        def install():
 
129
            do_custom_work()
 
130
 
 
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():
 
135
            pass
 
136
 
 
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
 
139
        # for you:
 
140
        # hooks = AnsibleHooks(
 
141
        #     'playbooks/my_machine_state.yaml',
 
142
        #     default_hooks=['config-changed', 'start', 'stop'])
 
143
 
 
144
        if __name__ == "__main__":
 
145
            # execute a hook based on the name the program is called by
 
146
            hooks.execute(sys.argv)
 
147
    """
 
148
 
 
149
    def __init__(self, playbook_path, default_hooks=None):
 
150
        """Register any hooks handled by ansible."""
 
151
        super(AnsibleHooks, self).__init__()
 
152
 
 
153
        self.playbook_path = playbook_path
 
154
 
 
155
        default_hooks = default_hooks or []
 
156
        noop = lambda *args, **kwargs: None
 
157
        for hook in default_hooks:
 
158
            self.register(hook, noop)
 
159
 
 
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])