~canonical-hw-cert/charms/xenial/snappy-device-agent/trunk

« back to all changes in this revision

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

  • Committer: Paul Larson
  • Date: 2016-05-16 20:27:32 UTC
  • Revision ID: paul.larson@canonical.com-20160516202732-9r4nkyl2f91w9xo3
Add support for xenial

Show diffs side-by-side

added added

removed removed

Lines of Context:
75
75
.. _playbooks: http://www.ansibleworks.com/docs/playbooks.html
76
76
.. _modules: http://www.ansibleworks.com/docs/modules.html
77
77
 
 
78
A further feature os the ansible hooks is to provide a light weight "action"
 
79
scripting tool. This is a decorator that you apply to a function, and that
 
80
function can now receive cli args, and can pass extra args to the playbook.
 
81
 
 
82
e.g.
 
83
 
 
84
 
 
85
@hooks.action()
 
86
def some_action(amount, force="False"):
 
87
    "Usage: some-action AMOUNT [force=True]"  # <-- shown on error
 
88
    # process the arguments
 
89
    # do some calls
 
90
    # return extra-vars to be passed to ansible-playbook
 
91
    return {
 
92
        'amount': int(amount),
 
93
        'type': force,
 
94
    }
 
95
 
 
96
You can now create a symlink to hooks.py that can be invoked like a hook, but
 
97
with cli params:
 
98
 
 
99
# link actions/some-action to hooks/hooks.py
 
100
 
 
101
actions/some-action amount=10 force=true
 
102
 
78
103
"""
79
104
import os
 
105
import stat
80
106
import subprocess
 
107
import functools
81
108
 
82
109
import charmhelpers.contrib.templating.contexts
83
110
import charmhelpers.core.host
112
139
        hosts_file.write('localhost ansible_connection=local')
113
140
 
114
141
 
115
 
def apply_playbook(playbook, tags=None):
 
142
def apply_playbook(playbook, tags=None, extra_vars=None):
116
143
    tags = tags or []
117
144
    tags = ",".join(tags)
118
145
    charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
119
146
        ansible_vars_path, namespace_separator='__',
120
 
        allow_hyphens_in_keys=False)
 
147
        allow_hyphens_in_keys=False, mode=(stat.S_IRUSR | stat.S_IWUSR))
 
148
 
121
149
    # we want ansible's log output to be unbuffered
122
150
    env = os.environ.copy()
123
151
    env['PYTHONUNBUFFERED'] = "1"
129
157
    ]
130
158
    if tags:
131
159
        call.extend(['--tags', '{}'.format(tags)])
 
160
    if extra_vars:
 
161
        extra = ["%s=%s" % (k, v) for k, v in extra_vars.items()]
 
162
        call.extend(['--extra-vars', " ".join(extra)])
132
163
    subprocess.check_call(call, env=env)
133
164
 
134
165
 
172
203
        """Register any hooks handled by ansible."""
173
204
        super(AnsibleHooks, self).__init__()
174
205
 
 
206
        self._actions = {}
175
207
        self.playbook_path = playbook_path
176
208
 
177
209
        default_hooks = default_hooks or []
182
214
        for hook in default_hooks:
183
215
            self.register(hook, noop)
184
216
 
 
217
    def register_action(self, name, function):
 
218
        """Register a hook"""
 
219
        self._actions[name] = function
 
220
 
185
221
    def execute(self, args):
186
222
        """Execute the hook followed by the playbook using the hook as tag."""
187
 
        super(AnsibleHooks, self).execute(args)
188
223
        hook_name = os.path.basename(args[0])
 
224
        extra_vars = None
 
225
        if hook_name in self._actions:
 
226
            extra_vars = self._actions[hook_name](args[1:])
 
227
        else:
 
228
            super(AnsibleHooks, self).execute(args)
 
229
 
189
230
        charmhelpers.contrib.ansible.apply_playbook(
190
 
            self.playbook_path, tags=[hook_name])
 
231
            self.playbook_path, tags=[hook_name], extra_vars=extra_vars)
 
232
 
 
233
    def action(self, *action_names):
 
234
        """Decorator, registering them as actions"""
 
235
        def action_wrapper(decorated):
 
236
 
 
237
            @functools.wraps(decorated)
 
238
            def wrapper(argv):
 
239
                kwargs = dict(arg.split('=') for arg in argv)
 
240
                try:
 
241
                    return decorated(**kwargs)
 
242
                except TypeError as e:
 
243
                    if decorated.__doc__:
 
244
                        e.args += (decorated.__doc__,)
 
245
                    raise
 
246
 
 
247
            self.register_action(decorated.__name__, wrapper)
 
248
            if '_' in decorated.__name__:
 
249
                self.register_action(
 
250
                    decorated.__name__.replace('_', '-'), wrapper)
 
251
 
 
252
            return wrapper
 
253
 
 
254
        return action_wrapper