Merge lp:~1chb1n/charms/trusty/nova-compute-power/kilo into lp:~james-page/charms/trusty/nova-compute-power/redux
- Trusty Tahr (14.04)
- kilo
- Merge into redux
Proposed by
Ryan Beisner
Status: | Merged |
---|---|
Merged at revision: | 120 |
Proposed branch: | lp:~1chb1n/charms/trusty/nova-compute-power/kilo |
Merge into: | lp:~james-page/charms/trusty/nova-compute-power/redux |
Diff against target: |
1018 lines (+949/-4) 10 files modified
Makefile (+8/-2) bin/charm_helpers_sync.py (+253/-0) charm-helpers.yaml (+2/-0) hooks/charmhelpers/contrib/charmsupport/__init__.py (+15/-0) hooks/charmhelpers/contrib/charmsupport/nrpe.py (+360/-0) hooks/charmhelpers/contrib/charmsupport/volumes.py (+175/-0) hooks/charmhelpers/contrib/python/__init__.py (+15/-0) hooks/charmhelpers/contrib/python/packages.py (+119/-0) templates/kilo/neutron.conf (+1/-1) templates/kilo/nova.conf (+1/-1) |
To merge this branch: | bzr merge lp:~1chb1n/charms/trusty/nova-compute-power/kilo |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Page | Pending | ||
Review via email: mp+261099@code.launchpad.net |
Commit message
Description of the change
Additional kilo enablement.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Makefile' | |||
2 | --- Makefile 2014-11-07 16:46:25 +0000 | |||
3 | +++ Makefile 2015-06-04 17:43:44 +0000 | |||
4 | @@ -5,5 +5,11 @@ | |||
5 | 5 | @flake8 --exclude hooks/charmhelpers hooks | 5 | @flake8 --exclude hooks/charmhelpers hooks |
6 | 6 | @charm proof | 6 | @charm proof |
7 | 7 | 7 | ||
10 | 8 | sync: | 8 | bin/charm_helpers_sync.py: |
11 | 9 | @charm-helper-sync -c charm-helpers.yaml | 9 | @mkdir -p bin |
12 | 10 | @bzr cat lp:charm-helpers/tools/charm_helpers_sync/charm_helpers_sync.py \ | ||
13 | 11 | > bin/charm_helpers_sync.py | ||
14 | 12 | |||
15 | 13 | sync: bin/charm_helpers_sync.py | ||
16 | 14 | @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers.yaml | ||
17 | 15 | |||
18 | 10 | 16 | ||
19 | === added directory 'bin' | |||
20 | === added file 'bin/charm_helpers_sync.py' | |||
21 | --- bin/charm_helpers_sync.py 1970-01-01 00:00:00 +0000 | |||
22 | +++ bin/charm_helpers_sync.py 2015-06-04 17:43:44 +0000 | |||
23 | @@ -0,0 +1,253 @@ | |||
24 | 1 | #!/usr/bin/python | ||
25 | 2 | |||
26 | 3 | # Copyright 2014-2015 Canonical Limited. | ||
27 | 4 | # | ||
28 | 5 | # This file is part of charm-helpers. | ||
29 | 6 | # | ||
30 | 7 | # charm-helpers is free software: you can redistribute it and/or modify | ||
31 | 8 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
32 | 9 | # published by the Free Software Foundation. | ||
33 | 10 | # | ||
34 | 11 | # charm-helpers is distributed in the hope that it will be useful, | ||
35 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
36 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
37 | 14 | # GNU Lesser General Public License for more details. | ||
38 | 15 | # | ||
39 | 16 | # You should have received a copy of the GNU Lesser General Public License | ||
40 | 17 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
41 | 18 | |||
42 | 19 | # Authors: | ||
43 | 20 | # Adam Gandelman <adamg@ubuntu.com> | ||
44 | 21 | |||
45 | 22 | import logging | ||
46 | 23 | import optparse | ||
47 | 24 | import os | ||
48 | 25 | import subprocess | ||
49 | 26 | import shutil | ||
50 | 27 | import sys | ||
51 | 28 | import tempfile | ||
52 | 29 | import yaml | ||
53 | 30 | from fnmatch import fnmatch | ||
54 | 31 | |||
55 | 32 | import six | ||
56 | 33 | |||
57 | 34 | CHARM_HELPERS_BRANCH = 'lp:charm-helpers' | ||
58 | 35 | |||
59 | 36 | |||
60 | 37 | def parse_config(conf_file): | ||
61 | 38 | if not os.path.isfile(conf_file): | ||
62 | 39 | logging.error('Invalid config file: %s.' % conf_file) | ||
63 | 40 | return False | ||
64 | 41 | return yaml.load(open(conf_file).read()) | ||
65 | 42 | |||
66 | 43 | |||
67 | 44 | def clone_helpers(work_dir, branch): | ||
68 | 45 | dest = os.path.join(work_dir, 'charm-helpers') | ||
69 | 46 | logging.info('Checking out %s to %s.' % (branch, dest)) | ||
70 | 47 | cmd = ['bzr', 'checkout', '--lightweight', branch, dest] | ||
71 | 48 | subprocess.check_call(cmd) | ||
72 | 49 | return dest | ||
73 | 50 | |||
74 | 51 | |||
75 | 52 | def _module_path(module): | ||
76 | 53 | return os.path.join(*module.split('.')) | ||
77 | 54 | |||
78 | 55 | |||
79 | 56 | def _src_path(src, module): | ||
80 | 57 | return os.path.join(src, 'charmhelpers', _module_path(module)) | ||
81 | 58 | |||
82 | 59 | |||
83 | 60 | def _dest_path(dest, module): | ||
84 | 61 | return os.path.join(dest, _module_path(module)) | ||
85 | 62 | |||
86 | 63 | |||
87 | 64 | def _is_pyfile(path): | ||
88 | 65 | return os.path.isfile(path + '.py') | ||
89 | 66 | |||
90 | 67 | |||
91 | 68 | def ensure_init(path): | ||
92 | 69 | ''' | ||
93 | 70 | ensure directories leading up to path are importable, omitting | ||
94 | 71 | parent directory, eg path='/hooks/helpers/foo'/: | ||
95 | 72 | hooks/ | ||
96 | 73 | hooks/helpers/__init__.py | ||
97 | 74 | hooks/helpers/foo/__init__.py | ||
98 | 75 | ''' | ||
99 | 76 | for d, dirs, files in os.walk(os.path.join(*path.split('/')[:2])): | ||
100 | 77 | _i = os.path.join(d, '__init__.py') | ||
101 | 78 | if not os.path.exists(_i): | ||
102 | 79 | logging.info('Adding missing __init__.py: %s' % _i) | ||
103 | 80 | open(_i, 'wb').close() | ||
104 | 81 | |||
105 | 82 | |||
106 | 83 | def sync_pyfile(src, dest): | ||
107 | 84 | src = src + '.py' | ||
108 | 85 | src_dir = os.path.dirname(src) | ||
109 | 86 | logging.info('Syncing pyfile: %s -> %s.' % (src, dest)) | ||
110 | 87 | if not os.path.exists(dest): | ||
111 | 88 | os.makedirs(dest) | ||
112 | 89 | shutil.copy(src, dest) | ||
113 | 90 | if os.path.isfile(os.path.join(src_dir, '__init__.py')): | ||
114 | 91 | shutil.copy(os.path.join(src_dir, '__init__.py'), | ||
115 | 92 | dest) | ||
116 | 93 | ensure_init(dest) | ||
117 | 94 | |||
118 | 95 | |||
119 | 96 | def get_filter(opts=None): | ||
120 | 97 | opts = opts or [] | ||
121 | 98 | if 'inc=*' in opts: | ||
122 | 99 | # do not filter any files, include everything | ||
123 | 100 | return None | ||
124 | 101 | |||
125 | 102 | def _filter(dir, ls): | ||
126 | 103 | incs = [opt.split('=').pop() for opt in opts if 'inc=' in opt] | ||
127 | 104 | _filter = [] | ||
128 | 105 | for f in ls: | ||
129 | 106 | _f = os.path.join(dir, f) | ||
130 | 107 | |||
131 | 108 | if not os.path.isdir(_f) and not _f.endswith('.py') and incs: | ||
132 | 109 | if True not in [fnmatch(_f, inc) for inc in incs]: | ||
133 | 110 | logging.debug('Not syncing %s, does not match include ' | ||
134 | 111 | 'filters (%s)' % (_f, incs)) | ||
135 | 112 | _filter.append(f) | ||
136 | 113 | else: | ||
137 | 114 | logging.debug('Including file, which matches include ' | ||
138 | 115 | 'filters (%s): %s' % (incs, _f)) | ||
139 | 116 | elif (os.path.isfile(_f) and not _f.endswith('.py')): | ||
140 | 117 | logging.debug('Not syncing file: %s' % f) | ||
141 | 118 | _filter.append(f) | ||
142 | 119 | elif (os.path.isdir(_f) and not | ||
143 | 120 | os.path.isfile(os.path.join(_f, '__init__.py'))): | ||
144 | 121 | logging.debug('Not syncing directory: %s' % f) | ||
145 | 122 | _filter.append(f) | ||
146 | 123 | return _filter | ||
147 | 124 | return _filter | ||
148 | 125 | |||
149 | 126 | |||
150 | 127 | def sync_directory(src, dest, opts=None): | ||
151 | 128 | if os.path.exists(dest): | ||
152 | 129 | logging.debug('Removing existing directory: %s' % dest) | ||
153 | 130 | shutil.rmtree(dest) | ||
154 | 131 | logging.info('Syncing directory: %s -> %s.' % (src, dest)) | ||
155 | 132 | |||
156 | 133 | shutil.copytree(src, dest, ignore=get_filter(opts)) | ||
157 | 134 | ensure_init(dest) | ||
158 | 135 | |||
159 | 136 | |||
160 | 137 | def sync(src, dest, module, opts=None): | ||
161 | 138 | |||
162 | 139 | # Sync charmhelpers/__init__.py for bootstrap code. | ||
163 | 140 | sync_pyfile(_src_path(src, '__init__'), dest) | ||
164 | 141 | |||
165 | 142 | # Sync other __init__.py files in the path leading to module. | ||
166 | 143 | m = [] | ||
167 | 144 | steps = module.split('.')[:-1] | ||
168 | 145 | while steps: | ||
169 | 146 | m.append(steps.pop(0)) | ||
170 | 147 | init = '.'.join(m + ['__init__']) | ||
171 | 148 | sync_pyfile(_src_path(src, init), | ||
172 | 149 | os.path.dirname(_dest_path(dest, init))) | ||
173 | 150 | |||
174 | 151 | # Sync the module, or maybe a .py file. | ||
175 | 152 | if os.path.isdir(_src_path(src, module)): | ||
176 | 153 | sync_directory(_src_path(src, module), _dest_path(dest, module), opts) | ||
177 | 154 | elif _is_pyfile(_src_path(src, module)): | ||
178 | 155 | sync_pyfile(_src_path(src, module), | ||
179 | 156 | os.path.dirname(_dest_path(dest, module))) | ||
180 | 157 | else: | ||
181 | 158 | logging.warn('Could not sync: %s. Neither a pyfile or directory, ' | ||
182 | 159 | 'does it even exist?' % module) | ||
183 | 160 | |||
184 | 161 | |||
185 | 162 | def parse_sync_options(options): | ||
186 | 163 | if not options: | ||
187 | 164 | return [] | ||
188 | 165 | return options.split(',') | ||
189 | 166 | |||
190 | 167 | |||
191 | 168 | def extract_options(inc, global_options=None): | ||
192 | 169 | global_options = global_options or [] | ||
193 | 170 | if global_options and isinstance(global_options, six.string_types): | ||
194 | 171 | global_options = [global_options] | ||
195 | 172 | if '|' not in inc: | ||
196 | 173 | return (inc, global_options) | ||
197 | 174 | inc, opts = inc.split('|') | ||
198 | 175 | return (inc, parse_sync_options(opts) + global_options) | ||
199 | 176 | |||
200 | 177 | |||
201 | 178 | def sync_helpers(include, src, dest, options=None): | ||
202 | 179 | if not os.path.isdir(dest): | ||
203 | 180 | os.makedirs(dest) | ||
204 | 181 | |||
205 | 182 | global_options = parse_sync_options(options) | ||
206 | 183 | |||
207 | 184 | for inc in include: | ||
208 | 185 | if isinstance(inc, str): | ||
209 | 186 | inc, opts = extract_options(inc, global_options) | ||
210 | 187 | sync(src, dest, inc, opts) | ||
211 | 188 | elif isinstance(inc, dict): | ||
212 | 189 | # could also do nested dicts here. | ||
213 | 190 | for k, v in six.iteritems(inc): | ||
214 | 191 | if isinstance(v, list): | ||
215 | 192 | for m in v: | ||
216 | 193 | inc, opts = extract_options(m, global_options) | ||
217 | 194 | sync(src, dest, '%s.%s' % (k, inc), opts) | ||
218 | 195 | |||
219 | 196 | if __name__ == '__main__': | ||
220 | 197 | parser = optparse.OptionParser() | ||
221 | 198 | parser.add_option('-c', '--config', action='store', dest='config', | ||
222 | 199 | default=None, help='helper config file') | ||
223 | 200 | parser.add_option('-D', '--debug', action='store_true', dest='debug', | ||
224 | 201 | default=False, help='debug') | ||
225 | 202 | parser.add_option('-b', '--branch', action='store', dest='branch', | ||
226 | 203 | help='charm-helpers bzr branch (overrides config)') | ||
227 | 204 | parser.add_option('-d', '--destination', action='store', dest='dest_dir', | ||
228 | 205 | help='sync destination dir (overrides config)') | ||
229 | 206 | (opts, args) = parser.parse_args() | ||
230 | 207 | |||
231 | 208 | if opts.debug: | ||
232 | 209 | logging.basicConfig(level=logging.DEBUG) | ||
233 | 210 | else: | ||
234 | 211 | logging.basicConfig(level=logging.INFO) | ||
235 | 212 | |||
236 | 213 | if opts.config: | ||
237 | 214 | logging.info('Loading charm helper config from %s.' % opts.config) | ||
238 | 215 | config = parse_config(opts.config) | ||
239 | 216 | if not config: | ||
240 | 217 | logging.error('Could not parse config from %s.' % opts.config) | ||
241 | 218 | sys.exit(1) | ||
242 | 219 | else: | ||
243 | 220 | config = {} | ||
244 | 221 | |||
245 | 222 | if 'branch' not in config: | ||
246 | 223 | config['branch'] = CHARM_HELPERS_BRANCH | ||
247 | 224 | if opts.branch: | ||
248 | 225 | config['branch'] = opts.branch | ||
249 | 226 | if opts.dest_dir: | ||
250 | 227 | config['destination'] = opts.dest_dir | ||
251 | 228 | |||
252 | 229 | if 'destination' not in config: | ||
253 | 230 | logging.error('No destination dir. specified as option or config.') | ||
254 | 231 | sys.exit(1) | ||
255 | 232 | |||
256 | 233 | if 'include' not in config: | ||
257 | 234 | if not args: | ||
258 | 235 | logging.error('No modules to sync specified as option or config.') | ||
259 | 236 | sys.exit(1) | ||
260 | 237 | config['include'] = [] | ||
261 | 238 | [config['include'].append(a) for a in args] | ||
262 | 239 | |||
263 | 240 | sync_options = None | ||
264 | 241 | if 'options' in config: | ||
265 | 242 | sync_options = config['options'] | ||
266 | 243 | tmpd = tempfile.mkdtemp() | ||
267 | 244 | try: | ||
268 | 245 | checkout = clone_helpers(tmpd, config['branch']) | ||
269 | 246 | sync_helpers(config['include'], checkout, config['destination'], | ||
270 | 247 | options=sync_options) | ||
271 | 248 | except Exception as e: | ||
272 | 249 | logging.error("Could not sync: %s" % e) | ||
273 | 250 | raise e | ||
274 | 251 | finally: | ||
275 | 252 | logging.debug('Cleaning up %s' % tmpd) | ||
276 | 253 | shutil.rmtree(tmpd) | ||
277 | 0 | 254 | ||
278 | === modified file 'charm-helpers.yaml' | |||
279 | --- charm-helpers.yaml 2014-11-07 16:42:23 +0000 | |||
280 | +++ charm-helpers.yaml 2015-06-04 17:43:44 +0000 | |||
281 | @@ -9,4 +9,6 @@ | |||
282 | 9 | - apache | 9 | - apache |
283 | 10 | - cluster | 10 | - cluster |
284 | 11 | - contrib.network | 11 | - contrib.network |
285 | 12 | - contrib.python.packages | ||
286 | 12 | - payload.execd | 13 | - payload.execd |
287 | 14 | - contrib.charmsupport | ||
288 | 13 | 15 | ||
289 | === added directory 'hooks/charmhelpers/contrib/charmsupport' | |||
290 | === added file 'hooks/charmhelpers/contrib/charmsupport/__init__.py' | |||
291 | --- hooks/charmhelpers/contrib/charmsupport/__init__.py 1970-01-01 00:00:00 +0000 | |||
292 | +++ hooks/charmhelpers/contrib/charmsupport/__init__.py 2015-06-04 17:43:44 +0000 | |||
293 | @@ -0,0 +1,15 @@ | |||
294 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
295 | 2 | # | ||
296 | 3 | # This file is part of charm-helpers. | ||
297 | 4 | # | ||
298 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | ||
299 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
300 | 7 | # published by the Free Software Foundation. | ||
301 | 8 | # | ||
302 | 9 | # charm-helpers is distributed in the hope that it will be useful, | ||
303 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
304 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
305 | 12 | # GNU Lesser General Public License for more details. | ||
306 | 13 | # | ||
307 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
308 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
309 | 0 | 16 | ||
310 | === added file 'hooks/charmhelpers/contrib/charmsupport/nrpe.py' | |||
311 | --- hooks/charmhelpers/contrib/charmsupport/nrpe.py 1970-01-01 00:00:00 +0000 | |||
312 | +++ hooks/charmhelpers/contrib/charmsupport/nrpe.py 2015-06-04 17:43:44 +0000 | |||
313 | @@ -0,0 +1,360 @@ | |||
314 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
315 | 2 | # | ||
316 | 3 | # This file is part of charm-helpers. | ||
317 | 4 | # | ||
318 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | ||
319 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
320 | 7 | # published by the Free Software Foundation. | ||
321 | 8 | # | ||
322 | 9 | # charm-helpers is distributed in the hope that it will be useful, | ||
323 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
324 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
325 | 12 | # GNU Lesser General Public License for more details. | ||
326 | 13 | # | ||
327 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
328 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
329 | 16 | |||
330 | 17 | """Compatibility with the nrpe-external-master charm""" | ||
331 | 18 | # Copyright 2012 Canonical Ltd. | ||
332 | 19 | # | ||
333 | 20 | # Authors: | ||
334 | 21 | # Matthew Wedgwood <matthew.wedgwood@canonical.com> | ||
335 | 22 | |||
336 | 23 | import subprocess | ||
337 | 24 | import pwd | ||
338 | 25 | import grp | ||
339 | 26 | import os | ||
340 | 27 | import glob | ||
341 | 28 | import shutil | ||
342 | 29 | import re | ||
343 | 30 | import shlex | ||
344 | 31 | import yaml | ||
345 | 32 | |||
346 | 33 | from charmhelpers.core.hookenv import ( | ||
347 | 34 | config, | ||
348 | 35 | local_unit, | ||
349 | 36 | log, | ||
350 | 37 | relation_ids, | ||
351 | 38 | relation_set, | ||
352 | 39 | relations_of_type, | ||
353 | 40 | ) | ||
354 | 41 | |||
355 | 42 | from charmhelpers.core.host import service | ||
356 | 43 | |||
357 | 44 | # This module adds compatibility with the nrpe-external-master and plain nrpe | ||
358 | 45 | # subordinate charms. To use it in your charm: | ||
359 | 46 | # | ||
360 | 47 | # 1. Update metadata.yaml | ||
361 | 48 | # | ||
362 | 49 | # provides: | ||
363 | 50 | # (...) | ||
364 | 51 | # nrpe-external-master: | ||
365 | 52 | # interface: nrpe-external-master | ||
366 | 53 | # scope: container | ||
367 | 54 | # | ||
368 | 55 | # and/or | ||
369 | 56 | # | ||
370 | 57 | # provides: | ||
371 | 58 | # (...) | ||
372 | 59 | # local-monitors: | ||
373 | 60 | # interface: local-monitors | ||
374 | 61 | # scope: container | ||
375 | 62 | |||
376 | 63 | # | ||
377 | 64 | # 2. Add the following to config.yaml | ||
378 | 65 | # | ||
379 | 66 | # nagios_context: | ||
380 | 67 | # default: "juju" | ||
381 | 68 | # type: string | ||
382 | 69 | # description: | | ||
383 | 70 | # Used by the nrpe subordinate charms. | ||
384 | 71 | # A string that will be prepended to instance name to set the host name | ||
385 | 72 | # in nagios. So for instance the hostname would be something like: | ||
386 | 73 | # juju-myservice-0 | ||
387 | 74 | # If you're running multiple environments with the same services in them | ||
388 | 75 | # this allows you to differentiate between them. | ||
389 | 76 | # nagios_servicegroups: | ||
390 | 77 | # default: "" | ||
391 | 78 | # type: string | ||
392 | 79 | # description: | | ||
393 | 80 | # A comma-separated list of nagios servicegroups. | ||
394 | 81 | # If left empty, the nagios_context will be used as the servicegroup | ||
395 | 82 | # | ||
396 | 83 | # 3. Add custom checks (Nagios plugins) to files/nrpe-external-master | ||
397 | 84 | # | ||
398 | 85 | # 4. Update your hooks.py with something like this: | ||
399 | 86 | # | ||
400 | 87 | # from charmsupport.nrpe import NRPE | ||
401 | 88 | # (...) | ||
402 | 89 | # def update_nrpe_config(): | ||
403 | 90 | # nrpe_compat = NRPE() | ||
404 | 91 | # nrpe_compat.add_check( | ||
405 | 92 | # shortname = "myservice", | ||
406 | 93 | # description = "Check MyService", | ||
407 | 94 | # check_cmd = "check_http -w 2 -c 10 http://localhost" | ||
408 | 95 | # ) | ||
409 | 96 | # nrpe_compat.add_check( | ||
410 | 97 | # "myservice_other", | ||
411 | 98 | # "Check for widget failures", | ||
412 | 99 | # check_cmd = "/srv/myapp/scripts/widget_check" | ||
413 | 100 | # ) | ||
414 | 101 | # nrpe_compat.write() | ||
415 | 102 | # | ||
416 | 103 | # def config_changed(): | ||
417 | 104 | # (...) | ||
418 | 105 | # update_nrpe_config() | ||
419 | 106 | # | ||
420 | 107 | # def nrpe_external_master_relation_changed(): | ||
421 | 108 | # update_nrpe_config() | ||
422 | 109 | # | ||
423 | 110 | # def local_monitors_relation_changed(): | ||
424 | 111 | # update_nrpe_config() | ||
425 | 112 | # | ||
426 | 113 | # 5. ln -s hooks.py nrpe-external-master-relation-changed | ||
427 | 114 | # ln -s hooks.py local-monitors-relation-changed | ||
428 | 115 | |||
429 | 116 | |||
430 | 117 | class CheckException(Exception): | ||
431 | 118 | pass | ||
432 | 119 | |||
433 | 120 | |||
434 | 121 | class Check(object): | ||
435 | 122 | shortname_re = '[A-Za-z0-9-_]+$' | ||
436 | 123 | service_template = (""" | ||
437 | 124 | #--------------------------------------------------- | ||
438 | 125 | # This file is Juju managed | ||
439 | 126 | #--------------------------------------------------- | ||
440 | 127 | define service {{ | ||
441 | 128 | use active-service | ||
442 | 129 | host_name {nagios_hostname} | ||
443 | 130 | service_description {nagios_hostname}[{shortname}] """ | ||
444 | 131 | """{description} | ||
445 | 132 | check_command check_nrpe!{command} | ||
446 | 133 | servicegroups {nagios_servicegroup} | ||
447 | 134 | }} | ||
448 | 135 | """) | ||
449 | 136 | |||
450 | 137 | def __init__(self, shortname, description, check_cmd): | ||
451 | 138 | super(Check, self).__init__() | ||
452 | 139 | # XXX: could be better to calculate this from the service name | ||
453 | 140 | if not re.match(self.shortname_re, shortname): | ||
454 | 141 | raise CheckException("shortname must match {}".format( | ||
455 | 142 | Check.shortname_re)) | ||
456 | 143 | self.shortname = shortname | ||
457 | 144 | self.command = "check_{}".format(shortname) | ||
458 | 145 | # Note: a set of invalid characters is defined by the | ||
459 | 146 | # Nagios server config | ||
460 | 147 | # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()= | ||
461 | 148 | self.description = description | ||
462 | 149 | self.check_cmd = self._locate_cmd(check_cmd) | ||
463 | 150 | |||
464 | 151 | def _locate_cmd(self, check_cmd): | ||
465 | 152 | search_path = ( | ||
466 | 153 | '/usr/lib/nagios/plugins', | ||
467 | 154 | '/usr/local/lib/nagios/plugins', | ||
468 | 155 | ) | ||
469 | 156 | parts = shlex.split(check_cmd) | ||
470 | 157 | for path in search_path: | ||
471 | 158 | if os.path.exists(os.path.join(path, parts[0])): | ||
472 | 159 | command = os.path.join(path, parts[0]) | ||
473 | 160 | if len(parts) > 1: | ||
474 | 161 | command += " " + " ".join(parts[1:]) | ||
475 | 162 | return command | ||
476 | 163 | log('Check command not found: {}'.format(parts[0])) | ||
477 | 164 | return '' | ||
478 | 165 | |||
479 | 166 | def write(self, nagios_context, hostname, nagios_servicegroups): | ||
480 | 167 | nrpe_check_file = '/etc/nagios/nrpe.d/{}.cfg'.format( | ||
481 | 168 | self.command) | ||
482 | 169 | with open(nrpe_check_file, 'w') as nrpe_check_config: | ||
483 | 170 | nrpe_check_config.write("# check {}\n".format(self.shortname)) | ||
484 | 171 | nrpe_check_config.write("command[{}]={}\n".format( | ||
485 | 172 | self.command, self.check_cmd)) | ||
486 | 173 | |||
487 | 174 | if not os.path.exists(NRPE.nagios_exportdir): | ||
488 | 175 | log('Not writing service config as {} is not accessible'.format( | ||
489 | 176 | NRPE.nagios_exportdir)) | ||
490 | 177 | else: | ||
491 | 178 | self.write_service_config(nagios_context, hostname, | ||
492 | 179 | nagios_servicegroups) | ||
493 | 180 | |||
494 | 181 | def write_service_config(self, nagios_context, hostname, | ||
495 | 182 | nagios_servicegroups): | ||
496 | 183 | for f in os.listdir(NRPE.nagios_exportdir): | ||
497 | 184 | if re.search('.*{}.cfg'.format(self.command), f): | ||
498 | 185 | os.remove(os.path.join(NRPE.nagios_exportdir, f)) | ||
499 | 186 | |||
500 | 187 | templ_vars = { | ||
501 | 188 | 'nagios_hostname': hostname, | ||
502 | 189 | 'nagios_servicegroup': nagios_servicegroups, | ||
503 | 190 | 'description': self.description, | ||
504 | 191 | 'shortname': self.shortname, | ||
505 | 192 | 'command': self.command, | ||
506 | 193 | } | ||
507 | 194 | nrpe_service_text = Check.service_template.format(**templ_vars) | ||
508 | 195 | nrpe_service_file = '{}/service__{}_{}.cfg'.format( | ||
509 | 196 | NRPE.nagios_exportdir, hostname, self.command) | ||
510 | 197 | with open(nrpe_service_file, 'w') as nrpe_service_config: | ||
511 | 198 | nrpe_service_config.write(str(nrpe_service_text)) | ||
512 | 199 | |||
513 | 200 | def run(self): | ||
514 | 201 | subprocess.call(self.check_cmd) | ||
515 | 202 | |||
516 | 203 | |||
517 | 204 | class NRPE(object): | ||
518 | 205 | nagios_logdir = '/var/log/nagios' | ||
519 | 206 | nagios_exportdir = '/var/lib/nagios/export' | ||
520 | 207 | nrpe_confdir = '/etc/nagios/nrpe.d' | ||
521 | 208 | |||
522 | 209 | def __init__(self, hostname=None): | ||
523 | 210 | super(NRPE, self).__init__() | ||
524 | 211 | self.config = config() | ||
525 | 212 | self.nagios_context = self.config['nagios_context'] | ||
526 | 213 | if 'nagios_servicegroups' in self.config and self.config['nagios_servicegroups']: | ||
527 | 214 | self.nagios_servicegroups = self.config['nagios_servicegroups'] | ||
528 | 215 | else: | ||
529 | 216 | self.nagios_servicegroups = self.nagios_context | ||
530 | 217 | self.unit_name = local_unit().replace('/', '-') | ||
531 | 218 | if hostname: | ||
532 | 219 | self.hostname = hostname | ||
533 | 220 | else: | ||
534 | 221 | self.hostname = "{}-{}".format(self.nagios_context, self.unit_name) | ||
535 | 222 | self.checks = [] | ||
536 | 223 | |||
537 | 224 | def add_check(self, *args, **kwargs): | ||
538 | 225 | self.checks.append(Check(*args, **kwargs)) | ||
539 | 226 | |||
540 | 227 | def write(self): | ||
541 | 228 | try: | ||
542 | 229 | nagios_uid = pwd.getpwnam('nagios').pw_uid | ||
543 | 230 | nagios_gid = grp.getgrnam('nagios').gr_gid | ||
544 | 231 | except: | ||
545 | 232 | log("Nagios user not set up, nrpe checks not updated") | ||
546 | 233 | return | ||
547 | 234 | |||
548 | 235 | if not os.path.exists(NRPE.nagios_logdir): | ||
549 | 236 | os.mkdir(NRPE.nagios_logdir) | ||
550 | 237 | os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid) | ||
551 | 238 | |||
552 | 239 | nrpe_monitors = {} | ||
553 | 240 | monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}} | ||
554 | 241 | for nrpecheck in self.checks: | ||
555 | 242 | nrpecheck.write(self.nagios_context, self.hostname, | ||
556 | 243 | self.nagios_servicegroups) | ||
557 | 244 | nrpe_monitors[nrpecheck.shortname] = { | ||
558 | 245 | "command": nrpecheck.command, | ||
559 | 246 | } | ||
560 | 247 | |||
561 | 248 | service('restart', 'nagios-nrpe-server') | ||
562 | 249 | |||
563 | 250 | monitor_ids = relation_ids("local-monitors") + \ | ||
564 | 251 | relation_ids("nrpe-external-master") | ||
565 | 252 | for rid in monitor_ids: | ||
566 | 253 | relation_set(relation_id=rid, monitors=yaml.dump(monitors)) | ||
567 | 254 | |||
568 | 255 | |||
569 | 256 | def get_nagios_hostcontext(relation_name='nrpe-external-master'): | ||
570 | 257 | """ | ||
571 | 258 | Query relation with nrpe subordinate, return the nagios_host_context | ||
572 | 259 | |||
573 | 260 | :param str relation_name: Name of relation nrpe sub joined to | ||
574 | 261 | """ | ||
575 | 262 | for rel in relations_of_type(relation_name): | ||
576 | 263 | if 'nagios_hostname' in rel: | ||
577 | 264 | return rel['nagios_host_context'] | ||
578 | 265 | |||
579 | 266 | |||
580 | 267 | def get_nagios_hostname(relation_name='nrpe-external-master'): | ||
581 | 268 | """ | ||
582 | 269 | Query relation with nrpe subordinate, return the nagios_hostname | ||
583 | 270 | |||
584 | 271 | :param str relation_name: Name of relation nrpe sub joined to | ||
585 | 272 | """ | ||
586 | 273 | for rel in relations_of_type(relation_name): | ||
587 | 274 | if 'nagios_hostname' in rel: | ||
588 | 275 | return rel['nagios_hostname'] | ||
589 | 276 | |||
590 | 277 | |||
591 | 278 | def get_nagios_unit_name(relation_name='nrpe-external-master'): | ||
592 | 279 | """ | ||
593 | 280 | Return the nagios unit name prepended with host_context if needed | ||
594 | 281 | |||
595 | 282 | :param str relation_name: Name of relation nrpe sub joined to | ||
596 | 283 | """ | ||
597 | 284 | host_context = get_nagios_hostcontext(relation_name) | ||
598 | 285 | if host_context: | ||
599 | 286 | unit = "%s:%s" % (host_context, local_unit()) | ||
600 | 287 | else: | ||
601 | 288 | unit = local_unit() | ||
602 | 289 | return unit | ||
603 | 290 | |||
604 | 291 | |||
605 | 292 | def add_init_service_checks(nrpe, services, unit_name): | ||
606 | 293 | """ | ||
607 | 294 | Add checks for each service in list | ||
608 | 295 | |||
609 | 296 | :param NRPE nrpe: NRPE object to add check to | ||
610 | 297 | :param list services: List of services to check | ||
611 | 298 | :param str unit_name: Unit name to use in check description | ||
612 | 299 | """ | ||
613 | 300 | for svc in services: | ||
614 | 301 | upstart_init = '/etc/init/%s.conf' % svc | ||
615 | 302 | sysv_init = '/etc/init.d/%s' % svc | ||
616 | 303 | if os.path.exists(upstart_init): | ||
617 | 304 | nrpe.add_check( | ||
618 | 305 | shortname=svc, | ||
619 | 306 | description='process check {%s}' % unit_name, | ||
620 | 307 | check_cmd='check_upstart_job %s' % svc | ||
621 | 308 | ) | ||
622 | 309 | elif os.path.exists(sysv_init): | ||
623 | 310 | cronpath = '/etc/cron.d/nagios-service-check-%s' % svc | ||
624 | 311 | cron_file = ('*/5 * * * * root ' | ||
625 | 312 | '/usr/local/lib/nagios/plugins/check_exit_status.pl ' | ||
626 | 313 | '-s /etc/init.d/%s status > ' | ||
627 | 314 | '/var/lib/nagios/service-check-%s.txt\n' % (svc, | ||
628 | 315 | svc) | ||
629 | 316 | ) | ||
630 | 317 | f = open(cronpath, 'w') | ||
631 | 318 | f.write(cron_file) | ||
632 | 319 | f.close() | ||
633 | 320 | nrpe.add_check( | ||
634 | 321 | shortname=svc, | ||
635 | 322 | description='process check {%s}' % unit_name, | ||
636 | 323 | check_cmd='check_status_file.py -f ' | ||
637 | 324 | '/var/lib/nagios/service-check-%s.txt' % svc, | ||
638 | 325 | ) | ||
639 | 326 | |||
640 | 327 | |||
641 | 328 | def copy_nrpe_checks(): | ||
642 | 329 | """ | ||
643 | 330 | Copy the nrpe checks into place | ||
644 | 331 | |||
645 | 332 | """ | ||
646 | 333 | NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins' | ||
647 | 334 | nrpe_files_dir = os.path.join(os.getenv('CHARM_DIR'), 'hooks', | ||
648 | 335 | 'charmhelpers', 'contrib', 'openstack', | ||
649 | 336 | 'files') | ||
650 | 337 | |||
651 | 338 | if not os.path.exists(NAGIOS_PLUGINS): | ||
652 | 339 | os.makedirs(NAGIOS_PLUGINS) | ||
653 | 340 | for fname in glob.glob(os.path.join(nrpe_files_dir, "check_*")): | ||
654 | 341 | if os.path.isfile(fname): | ||
655 | 342 | shutil.copy2(fname, | ||
656 | 343 | os.path.join(NAGIOS_PLUGINS, os.path.basename(fname))) | ||
657 | 344 | |||
658 | 345 | |||
659 | 346 | def add_haproxy_checks(nrpe, unit_name): | ||
660 | 347 | """ | ||
661 | 348 | Add checks for each service in list | ||
662 | 349 | |||
663 | 350 | :param NRPE nrpe: NRPE object to add check to | ||
664 | 351 | :param str unit_name: Unit name to use in check description | ||
665 | 352 | """ | ||
666 | 353 | nrpe.add_check( | ||
667 | 354 | shortname='haproxy_servers', | ||
668 | 355 | description='Check HAProxy {%s}' % unit_name, | ||
669 | 356 | check_cmd='check_haproxy.sh') | ||
670 | 357 | nrpe.add_check( | ||
671 | 358 | shortname='haproxy_queue', | ||
672 | 359 | description='Check HAProxy queue depth {%s}' % unit_name, | ||
673 | 360 | check_cmd='check_haproxy_queue_depth.sh') | ||
674 | 0 | 361 | ||
675 | === added file 'hooks/charmhelpers/contrib/charmsupport/volumes.py' | |||
676 | --- hooks/charmhelpers/contrib/charmsupport/volumes.py 1970-01-01 00:00:00 +0000 | |||
677 | +++ hooks/charmhelpers/contrib/charmsupport/volumes.py 2015-06-04 17:43:44 +0000 | |||
678 | @@ -0,0 +1,175 @@ | |||
679 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
680 | 2 | # | ||
681 | 3 | # This file is part of charm-helpers. | ||
682 | 4 | # | ||
683 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | ||
684 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
685 | 7 | # published by the Free Software Foundation. | ||
686 | 8 | # | ||
687 | 9 | # charm-helpers is distributed in the hope that it will be useful, | ||
688 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
689 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
690 | 12 | # GNU Lesser General Public License for more details. | ||
691 | 13 | # | ||
692 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
693 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
694 | 16 | |||
695 | 17 | ''' | ||
696 | 18 | Functions for managing volumes in juju units. One volume is supported per unit. | ||
697 | 19 | Subordinates may have their own storage, provided it is on its own partition. | ||
698 | 20 | |||
699 | 21 | Configuration stanzas:: | ||
700 | 22 | |||
701 | 23 | volume-ephemeral: | ||
702 | 24 | type: boolean | ||
703 | 25 | default: true | ||
704 | 26 | description: > | ||
705 | 27 | If false, a volume is mounted as sepecified in "volume-map" | ||
706 | 28 | If true, ephemeral storage will be used, meaning that log data | ||
707 | 29 | will only exist as long as the machine. YOU HAVE BEEN WARNED. | ||
708 | 30 | volume-map: | ||
709 | 31 | type: string | ||
710 | 32 | default: {} | ||
711 | 33 | description: > | ||
712 | 34 | YAML map of units to device names, e.g: | ||
713 | 35 | "{ rsyslog/0: /dev/vdb, rsyslog/1: /dev/vdb }" | ||
714 | 36 | Service units will raise a configure-error if volume-ephemeral | ||
715 | 37 | is 'true' and no volume-map value is set. Use 'juju set' to set a | ||
716 | 38 | value and 'juju resolved' to complete configuration. | ||
717 | 39 | |||
718 | 40 | Usage:: | ||
719 | 41 | |||
720 | 42 | from charmsupport.volumes import configure_volume, VolumeConfigurationError | ||
721 | 43 | from charmsupport.hookenv import log, ERROR | ||
722 | 44 | def post_mount_hook(): | ||
723 | 45 | stop_service('myservice') | ||
724 | 46 | def post_mount_hook(): | ||
725 | 47 | start_service('myservice') | ||
726 | 48 | |||
727 | 49 | if __name__ == '__main__': | ||
728 | 50 | try: | ||
729 | 51 | configure_volume(before_change=pre_mount_hook, | ||
730 | 52 | after_change=post_mount_hook) | ||
731 | 53 | except VolumeConfigurationError: | ||
732 | 54 | log('Storage could not be configured', ERROR) | ||
733 | 55 | |||
734 | 56 | ''' | ||
735 | 57 | |||
736 | 58 | # XXX: Known limitations | ||
737 | 59 | # - fstab is neither consulted nor updated | ||
738 | 60 | |||
739 | 61 | import os | ||
740 | 62 | from charmhelpers.core import hookenv | ||
741 | 63 | from charmhelpers.core import host | ||
742 | 64 | import yaml | ||
743 | 65 | |||
744 | 66 | |||
745 | 67 | MOUNT_BASE = '/srv/juju/volumes' | ||
746 | 68 | |||
747 | 69 | |||
748 | 70 | class VolumeConfigurationError(Exception): | ||
749 | 71 | '''Volume configuration data is missing or invalid''' | ||
750 | 72 | pass | ||
751 | 73 | |||
752 | 74 | |||
753 | 75 | def get_config(): | ||
754 | 76 | '''Gather and sanity-check volume configuration data''' | ||
755 | 77 | volume_config = {} | ||
756 | 78 | config = hookenv.config() | ||
757 | 79 | |||
758 | 80 | errors = False | ||
759 | 81 | |||
760 | 82 | if config.get('volume-ephemeral') in (True, 'True', 'true', 'Yes', 'yes'): | ||
761 | 83 | volume_config['ephemeral'] = True | ||
762 | 84 | else: | ||
763 | 85 | volume_config['ephemeral'] = False | ||
764 | 86 | |||
765 | 87 | try: | ||
766 | 88 | volume_map = yaml.safe_load(config.get('volume-map', '{}')) | ||
767 | 89 | except yaml.YAMLError as e: | ||
768 | 90 | hookenv.log("Error parsing YAML volume-map: {}".format(e), | ||
769 | 91 | hookenv.ERROR) | ||
770 | 92 | errors = True | ||
771 | 93 | if volume_map is None: | ||
772 | 94 | # probably an empty string | ||
773 | 95 | volume_map = {} | ||
774 | 96 | elif not isinstance(volume_map, dict): | ||
775 | 97 | hookenv.log("Volume-map should be a dictionary, not {}".format( | ||
776 | 98 | type(volume_map))) | ||
777 | 99 | errors = True | ||
778 | 100 | |||
779 | 101 | volume_config['device'] = volume_map.get(os.environ['JUJU_UNIT_NAME']) | ||
780 | 102 | if volume_config['device'] and volume_config['ephemeral']: | ||
781 | 103 | # asked for ephemeral storage but also defined a volume ID | ||
782 | 104 | hookenv.log('A volume is defined for this unit, but ephemeral ' | ||
783 | 105 | 'storage was requested', hookenv.ERROR) | ||
784 | 106 | errors = True | ||
785 | 107 | elif not volume_config['device'] and not volume_config['ephemeral']: | ||
786 | 108 | # asked for permanent storage but did not define volume ID | ||
787 | 109 | hookenv.log('Ephemeral storage was requested, but there is no volume ' | ||
788 | 110 | 'defined for this unit.', hookenv.ERROR) | ||
789 | 111 | errors = True | ||
790 | 112 | |||
791 | 113 | unit_mount_name = hookenv.local_unit().replace('/', '-') | ||
792 | 114 | volume_config['mountpoint'] = os.path.join(MOUNT_BASE, unit_mount_name) | ||
793 | 115 | |||
794 | 116 | if errors: | ||
795 | 117 | return None | ||
796 | 118 | return volume_config | ||
797 | 119 | |||
798 | 120 | |||
799 | 121 | def mount_volume(config): | ||
800 | 122 | if os.path.exists(config['mountpoint']): | ||
801 | 123 | if not os.path.isdir(config['mountpoint']): | ||
802 | 124 | hookenv.log('Not a directory: {}'.format(config['mountpoint'])) | ||
803 | 125 | raise VolumeConfigurationError() | ||
804 | 126 | else: | ||
805 | 127 | host.mkdir(config['mountpoint']) | ||
806 | 128 | if os.path.ismount(config['mountpoint']): | ||
807 | 129 | unmount_volume(config) | ||
808 | 130 | if not host.mount(config['device'], config['mountpoint'], persist=True): | ||
809 | 131 | raise VolumeConfigurationError() | ||
810 | 132 | |||
811 | 133 | |||
812 | 134 | def unmount_volume(config): | ||
813 | 135 | if os.path.ismount(config['mountpoint']): | ||
814 | 136 | if not host.umount(config['mountpoint'], persist=True): | ||
815 | 137 | raise VolumeConfigurationError() | ||
816 | 138 | |||
817 | 139 | |||
818 | 140 | def managed_mounts(): | ||
819 | 141 | '''List of all mounted managed volumes''' | ||
820 | 142 | return filter(lambda mount: mount[0].startswith(MOUNT_BASE), host.mounts()) | ||
821 | 143 | |||
822 | 144 | |||
823 | 145 | def configure_volume(before_change=lambda: None, after_change=lambda: None): | ||
824 | 146 | '''Set up storage (or don't) according to the charm's volume configuration. | ||
825 | 147 | Returns the mount point or "ephemeral". before_change and after_change | ||
826 | 148 | are optional functions to be called if the volume configuration changes. | ||
827 | 149 | ''' | ||
828 | 150 | |||
829 | 151 | config = get_config() | ||
830 | 152 | if not config: | ||
831 | 153 | hookenv.log('Failed to read volume configuration', hookenv.CRITICAL) | ||
832 | 154 | raise VolumeConfigurationError() | ||
833 | 155 | |||
834 | 156 | if config['ephemeral']: | ||
835 | 157 | if os.path.ismount(config['mountpoint']): | ||
836 | 158 | before_change() | ||
837 | 159 | unmount_volume(config) | ||
838 | 160 | after_change() | ||
839 | 161 | return 'ephemeral' | ||
840 | 162 | else: | ||
841 | 163 | # persistent storage | ||
842 | 164 | if os.path.ismount(config['mountpoint']): | ||
843 | 165 | mounts = dict(managed_mounts()) | ||
844 | 166 | if mounts.get(config['mountpoint']) != config['device']: | ||
845 | 167 | before_change() | ||
846 | 168 | unmount_volume(config) | ||
847 | 169 | mount_volume(config) | ||
848 | 170 | after_change() | ||
849 | 171 | else: | ||
850 | 172 | before_change() | ||
851 | 173 | mount_volume(config) | ||
852 | 174 | after_change() | ||
853 | 175 | return config['mountpoint'] | ||
854 | 0 | 176 | ||
855 | === added directory 'hooks/charmhelpers/contrib/python' | |||
856 | === added file 'hooks/charmhelpers/contrib/python/__init__.py' | |||
857 | --- hooks/charmhelpers/contrib/python/__init__.py 1970-01-01 00:00:00 +0000 | |||
858 | +++ hooks/charmhelpers/contrib/python/__init__.py 2015-06-04 17:43:44 +0000 | |||
859 | @@ -0,0 +1,15 @@ | |||
860 | 1 | # Copyright 2014-2015 Canonical Limited. | ||
861 | 2 | # | ||
862 | 3 | # This file is part of charm-helpers. | ||
863 | 4 | # | ||
864 | 5 | # charm-helpers is free software: you can redistribute it and/or modify | ||
865 | 6 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
866 | 7 | # published by the Free Software Foundation. | ||
867 | 8 | # | ||
868 | 9 | # charm-helpers is distributed in the hope that it will be useful, | ||
869 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
870 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
871 | 12 | # GNU Lesser General Public License for more details. | ||
872 | 13 | # | ||
873 | 14 | # You should have received a copy of the GNU Lesser General Public License | ||
874 | 15 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
875 | 0 | 16 | ||
876 | === added file 'hooks/charmhelpers/contrib/python/packages.py' | |||
877 | --- hooks/charmhelpers/contrib/python/packages.py 1970-01-01 00:00:00 +0000 | |||
878 | +++ hooks/charmhelpers/contrib/python/packages.py 2015-06-04 17:43:44 +0000 | |||
879 | @@ -0,0 +1,119 @@ | |||
880 | 1 | #!/usr/bin/env python | ||
881 | 2 | # coding: utf-8 | ||
882 | 3 | |||
883 | 4 | # Copyright 2014-2015 Canonical Limited. | ||
884 | 5 | # | ||
885 | 6 | # This file is part of charm-helpers. | ||
886 | 7 | # | ||
887 | 8 | # charm-helpers is free software: you can redistribute it and/or modify | ||
888 | 9 | # it under the terms of the GNU Lesser General Public License version 3 as | ||
889 | 10 | # published by the Free Software Foundation. | ||
890 | 11 | # | ||
891 | 12 | # charm-helpers is distributed in the hope that it will be useful, | ||
892 | 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
893 | 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
894 | 15 | # GNU Lesser General Public License for more details. | ||
895 | 16 | # | ||
896 | 17 | # You should have received a copy of the GNU Lesser General Public License | ||
897 | 18 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. | ||
898 | 19 | |||
899 | 20 | import os | ||
900 | 21 | import subprocess | ||
901 | 22 | |||
902 | 23 | from charmhelpers.fetch import apt_install, apt_update | ||
903 | 24 | from charmhelpers.core.hookenv import charm_dir, log | ||
904 | 25 | |||
905 | 26 | try: | ||
906 | 27 | from pip import main as pip_execute | ||
907 | 28 | except ImportError: | ||
908 | 29 | apt_update() | ||
909 | 30 | apt_install('python-pip') | ||
910 | 31 | from pip import main as pip_execute | ||
911 | 32 | |||
912 | 33 | __author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" | ||
913 | 34 | |||
914 | 35 | |||
915 | 36 | def parse_options(given, available): | ||
916 | 37 | """Given a set of options, check if available""" | ||
917 | 38 | for key, value in sorted(given.items()): | ||
918 | 39 | if key in available: | ||
919 | 40 | yield "--{0}={1}".format(key, value) | ||
920 | 41 | |||
921 | 42 | |||
922 | 43 | def pip_install_requirements(requirements, **options): | ||
923 | 44 | """Install a requirements file """ | ||
924 | 45 | command = ["install"] | ||
925 | 46 | |||
926 | 47 | available_options = ('proxy', 'src', 'log', ) | ||
927 | 48 | for option in parse_options(options, available_options): | ||
928 | 49 | command.append(option) | ||
929 | 50 | |||
930 | 51 | command.append("-r {0}".format(requirements)) | ||
931 | 52 | log("Installing from file: {} with options: {}".format(requirements, | ||
932 | 53 | command)) | ||
933 | 54 | pip_execute(command) | ||
934 | 55 | |||
935 | 56 | |||
936 | 57 | def pip_install(package, fatal=False, upgrade=False, venv=None, **options): | ||
937 | 58 | """Install a python package""" | ||
938 | 59 | if venv: | ||
939 | 60 | venv_python = os.path.join(venv, 'bin/pip') | ||
940 | 61 | command = [venv_python, "install"] | ||
941 | 62 | else: | ||
942 | 63 | command = ["install"] | ||
943 | 64 | |||
944 | 65 | available_options = ('proxy', 'src', 'log', 'index-url', ) | ||
945 | 66 | for option in parse_options(options, available_options): | ||
946 | 67 | command.append(option) | ||
947 | 68 | |||
948 | 69 | if upgrade: | ||
949 | 70 | command.append('--upgrade') | ||
950 | 71 | |||
951 | 72 | if isinstance(package, list): | ||
952 | 73 | command.extend(package) | ||
953 | 74 | else: | ||
954 | 75 | command.append(package) | ||
955 | 76 | |||
956 | 77 | log("Installing {} package with options: {}".format(package, | ||
957 | 78 | command)) | ||
958 | 79 | if venv: | ||
959 | 80 | subprocess.check_call(command) | ||
960 | 81 | else: | ||
961 | 82 | pip_execute(command) | ||
962 | 83 | |||
963 | 84 | |||
964 | 85 | def pip_uninstall(package, **options): | ||
965 | 86 | """Uninstall a python package""" | ||
966 | 87 | command = ["uninstall", "-q", "-y"] | ||
967 | 88 | |||
968 | 89 | available_options = ('proxy', 'log', ) | ||
969 | 90 | for option in parse_options(options, available_options): | ||
970 | 91 | command.append(option) | ||
971 | 92 | |||
972 | 93 | if isinstance(package, list): | ||
973 | 94 | command.extend(package) | ||
974 | 95 | else: | ||
975 | 96 | command.append(package) | ||
976 | 97 | |||
977 | 98 | log("Uninstalling {} package with options: {}".format(package, | ||
978 | 99 | command)) | ||
979 | 100 | pip_execute(command) | ||
980 | 101 | |||
981 | 102 | |||
982 | 103 | def pip_list(): | ||
983 | 104 | """Returns the list of current python installed packages | ||
984 | 105 | """ | ||
985 | 106 | return pip_execute(["list"]) | ||
986 | 107 | |||
987 | 108 | |||
988 | 109 | def pip_create_virtualenv(path=None): | ||
989 | 110 | """Create an isolated Python environment.""" | ||
990 | 111 | apt_install('python-virtualenv') | ||
991 | 112 | |||
992 | 113 | if path: | ||
993 | 114 | venv_path = path | ||
994 | 115 | else: | ||
995 | 116 | venv_path = os.path.join(charm_dir(), 'venv') | ||
996 | 117 | |||
997 | 118 | if not os.path.exists(venv_path): | ||
998 | 119 | subprocess.check_call(['virtualenv', venv_path]) | ||
999 | 0 | 120 | ||
1000 | === modified file 'templates/kilo/neutron.conf' | |||
1001 | --- templates/kilo/neutron.conf 2015-06-04 11:44:35 +0000 | |||
1002 | +++ templates/kilo/neutron.conf 2015-06-04 17:43:44 +0000 | |||
1003 | @@ -1,4 +1,4 @@ | |||
1005 | 1 | # icehouse | 1 | # kilo |
1006 | 2 | ############################################################################### | 2 | ############################################################################### |
1007 | 3 | # [ WARNING ] | 3 | # [ WARNING ] |
1008 | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. |
1009 | 5 | 5 | ||
1010 | === modified file 'templates/kilo/nova.conf' | |||
1011 | --- templates/kilo/nova.conf 2015-06-04 11:44:35 +0000 | |||
1012 | +++ templates/kilo/nova.conf 2015-06-04 17:43:44 +0000 | |||
1013 | @@ -1,4 +1,4 @@ | |||
1015 | 1 | # icehouse | 1 | # kilo |
1016 | 2 | ############################################################################### | 2 | ############################################################################### |
1017 | 3 | # [ WARNING ] | 3 | # [ WARNING ] |
1018 | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. | 4 | # Configuration file maintained by Juju. Local changes may be overwritten. |