Merge lp:~hopem/charms/trusty/keystone/sync-charm-helpers-get-bool-from-str into lp:~openstack-charmers-archive/charms/trusty/keystone/next
- Trusty Tahr (14.04)
- sync-charm-helpers-get-bool-from-str
- Merge into next
Status: | Merged |
---|---|
Merged at revision: | 119 |
Proposed branch: | lp:~hopem/charms/trusty/keystone/sync-charm-helpers-get-bool-from-str |
Merge into: | lp:~openstack-charmers-archive/charms/trusty/keystone/next |
Diff against target: |
851 lines (+575/-41) 12 files modified
hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+5/-2) hooks/charmhelpers/contrib/python/packages.py (+2/-2) hooks/charmhelpers/core/fstab.py (+2/-2) hooks/charmhelpers/core/strutils.py (+42/-0) hooks/charmhelpers/core/sysctl.py (+2/-2) hooks/charmhelpers/core/unitdata.py (+477/-0) hooks/charmhelpers/fetch/archiveurl.py (+10/-10) hooks/charmhelpers/fetch/giturl.py (+1/-1) hooks/keystone_context.py (+13/-4) hooks/keystone_hooks.py (+6/-3) hooks/keystone_utils.py (+10/-13) tests/charmhelpers/contrib/openstack/amulet/deployment.py (+5/-2) |
To merge this branch: | bzr merge lp:~hopem/charms/trusty/keystone/sync-charm-helpers-get-bool-from-str |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Liam Young (community) | Approve | ||
Review via email: mp+249801@code.launchpad.net |
Commit message
Description of the change
uosci-testing-bot (uosci-testing-bot) wrote : | # |
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #1820 keystone-next for hopem mp249801
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #1968 keystone-next for hopem mp249801
AMULET FAIL: amulet-test failed
AMULET Results (max last 2 lines):
ERROR subprocess encountered error code 1
make: *** [test] Error 1
Full amulet test output: http://
Build: http://
- 120. By Edward Hope-Morley
-
re-sync ch
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #2031 keystone-next for hopem mp249801
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #1821 keystone-next for hopem mp249801
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #1969 keystone-next for hopem mp249801
AMULET OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #2033 keystone-next for hopem mp249801
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #1823 keystone-next for hopem mp249801
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #1971 keystone-next for hopem mp249801
AMULET OK: passed
Build: http://
Preview Diff
1 | === modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py' |
2 | --- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-01-26 09:44:47 +0000 |
3 | +++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-02-16 12:07:04 +0000 |
4 | @@ -71,16 +71,19 @@ |
5 | services.append(this_service) |
6 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', |
7 | 'ceph-osd', 'ceph-radosgw'] |
8 | + # Openstack subordinate charms do not expose an origin option as that |
9 | + # is controlled by the principle |
10 | + ignore = ['neutron-openvswitch'] |
11 | |
12 | if self.openstack: |
13 | for svc in services: |
14 | - if svc['name'] not in use_source: |
15 | + if svc['name'] not in use_source + ignore: |
16 | config = {'openstack-origin': self.openstack} |
17 | self.d.configure(svc['name'], config) |
18 | |
19 | if self.source: |
20 | for svc in services: |
21 | - if svc['name'] in use_source: |
22 | + if svc['name'] in use_source and svc['name'] not in ignore: |
23 | config = {'source': self.source} |
24 | self.d.configure(svc['name'], config) |
25 | |
26 | |
27 | === modified file 'hooks/charmhelpers/contrib/python/packages.py' |
28 | --- hooks/charmhelpers/contrib/python/packages.py 2015-01-26 09:44:47 +0000 |
29 | +++ hooks/charmhelpers/contrib/python/packages.py 2015-02-16 12:07:04 +0000 |
30 | @@ -17,8 +17,6 @@ |
31 | # You should have received a copy of the GNU Lesser General Public License |
32 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
33 | |
34 | -__author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" |
35 | - |
36 | from charmhelpers.fetch import apt_install, apt_update |
37 | from charmhelpers.core.hookenv import log |
38 | |
39 | @@ -29,6 +27,8 @@ |
40 | apt_install('python-pip') |
41 | from pip import main as pip_execute |
42 | |
43 | +__author__ = "Jorge Niedbalski <jorge.niedbalski@canonical.com>" |
44 | + |
45 | |
46 | def parse_options(given, available): |
47 | """Given a set of options, check if available""" |
48 | |
49 | === modified file 'hooks/charmhelpers/core/fstab.py' |
50 | --- hooks/charmhelpers/core/fstab.py 2015-01-26 09:44:47 +0000 |
51 | +++ hooks/charmhelpers/core/fstab.py 2015-02-16 12:07:04 +0000 |
52 | @@ -17,11 +17,11 @@ |
53 | # You should have received a copy of the GNU Lesser General Public License |
54 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
55 | |
56 | -__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' |
57 | - |
58 | import io |
59 | import os |
60 | |
61 | +__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' |
62 | + |
63 | |
64 | class Fstab(io.FileIO): |
65 | """This class extends file in order to implement a file reader/writer |
66 | |
67 | === added file 'hooks/charmhelpers/core/strutils.py' |
68 | --- hooks/charmhelpers/core/strutils.py 1970-01-01 00:00:00 +0000 |
69 | +++ hooks/charmhelpers/core/strutils.py 2015-02-16 12:07:04 +0000 |
70 | @@ -0,0 +1,42 @@ |
71 | +#!/usr/bin/env python |
72 | +# -*- coding: utf-8 -*- |
73 | + |
74 | +# Copyright 2014-2015 Canonical Limited. |
75 | +# |
76 | +# This file is part of charm-helpers. |
77 | +# |
78 | +# charm-helpers is free software: you can redistribute it and/or modify |
79 | +# it under the terms of the GNU Lesser General Public License version 3 as |
80 | +# published by the Free Software Foundation. |
81 | +# |
82 | +# charm-helpers is distributed in the hope that it will be useful, |
83 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
84 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
85 | +# GNU Lesser General Public License for more details. |
86 | +# |
87 | +# You should have received a copy of the GNU Lesser General Public License |
88 | +# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
89 | + |
90 | +import six |
91 | + |
92 | + |
93 | +def bool_from_string(value): |
94 | + """Interpret string value as boolean. |
95 | + |
96 | + Returns True if value translates to True otherwise False. |
97 | + """ |
98 | + if isinstance(value, six.string_types): |
99 | + value = six.text_type(value) |
100 | + else: |
101 | + msg = "Unable to interpret non-string value '%s' as boolean" % (value) |
102 | + raise ValueError(msg) |
103 | + |
104 | + value = value.strip().lower() |
105 | + |
106 | + if value in ['y', 'yes', 'true', 't']: |
107 | + return True |
108 | + elif value in ['n', 'no', 'false', 'f']: |
109 | + return False |
110 | + |
111 | + msg = "Unable to interpret string value '%s' as boolean" % (value) |
112 | + raise ValueError(msg) |
113 | |
114 | === modified file 'hooks/charmhelpers/core/sysctl.py' |
115 | --- hooks/charmhelpers/core/sysctl.py 2015-02-03 12:56:40 +0000 |
116 | +++ hooks/charmhelpers/core/sysctl.py 2015-02-16 12:07:04 +0000 |
117 | @@ -17,8 +17,6 @@ |
118 | # You should have received a copy of the GNU Lesser General Public License |
119 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
120 | |
121 | -__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' |
122 | - |
123 | import yaml |
124 | |
125 | from subprocess import check_call |
126 | @@ -29,6 +27,8 @@ |
127 | ERROR, |
128 | ) |
129 | |
130 | +__author__ = 'Jorge Niedbalski R. <jorge.niedbalski@canonical.com>' |
131 | + |
132 | |
133 | def create(sysctl_dict, sysctl_file): |
134 | """Creates a sysctl.conf file from a YAML associative array |
135 | |
136 | === added file 'hooks/charmhelpers/core/unitdata.py' |
137 | --- hooks/charmhelpers/core/unitdata.py 1970-01-01 00:00:00 +0000 |
138 | +++ hooks/charmhelpers/core/unitdata.py 2015-02-16 12:07:04 +0000 |
139 | @@ -0,0 +1,477 @@ |
140 | +#!/usr/bin/env python |
141 | +# -*- coding: utf-8 -*- |
142 | +# |
143 | +# Copyright 2014-2015 Canonical Limited. |
144 | +# |
145 | +# This file is part of charm-helpers. |
146 | +# |
147 | +# charm-helpers is free software: you can redistribute it and/or modify |
148 | +# it under the terms of the GNU Lesser General Public License version 3 as |
149 | +# published by the Free Software Foundation. |
150 | +# |
151 | +# charm-helpers is distributed in the hope that it will be useful, |
152 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
153 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
154 | +# GNU Lesser General Public License for more details. |
155 | +# |
156 | +# You should have received a copy of the GNU Lesser General Public License |
157 | +# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
158 | +# |
159 | +# |
160 | +# Authors: |
161 | +# Kapil Thangavelu <kapil.foss@gmail.com> |
162 | +# |
163 | +""" |
164 | +Intro |
165 | +----- |
166 | + |
167 | +A simple way to store state in units. This provides a key value |
168 | +storage with support for versioned, transactional operation, |
169 | +and can calculate deltas from previous values to simplify unit logic |
170 | +when processing changes. |
171 | + |
172 | + |
173 | +Hook Integration |
174 | +---------------- |
175 | + |
176 | +There are several extant frameworks for hook execution, including |
177 | + |
178 | + - charmhelpers.core.hookenv.Hooks |
179 | + - charmhelpers.core.services.ServiceManager |
180 | + |
181 | +The storage classes are framework agnostic, one simple integration is |
182 | +via the HookData contextmanager. It will record the current hook |
183 | +execution environment (including relation data, config data, etc.), |
184 | +setup a transaction and allow easy access to the changes from |
185 | +previously seen values. One consequence of the integration is the |
186 | +reservation of particular keys ('rels', 'unit', 'env', 'config', |
187 | +'charm_revisions') for their respective values. |
188 | + |
189 | +Here's a fully worked integration example using hookenv.Hooks:: |
190 | + |
191 | + from charmhelper.core import hookenv, unitdata |
192 | + |
193 | + hook_data = unitdata.HookData() |
194 | + db = unitdata.kv() |
195 | + hooks = hookenv.Hooks() |
196 | + |
197 | + @hooks.hook |
198 | + def config_changed(): |
199 | + # Print all changes to configuration from previously seen |
200 | + # values. |
201 | + for changed, (prev, cur) in hook_data.conf.items(): |
202 | + print('config changed', changed, |
203 | + 'previous value', prev, |
204 | + 'current value', cur) |
205 | + |
206 | + # Get some unit specific bookeeping |
207 | + if not db.get('pkg_key'): |
208 | + key = urllib.urlopen('https://example.com/pkg_key').read() |
209 | + db.set('pkg_key', key) |
210 | + |
211 | + # Directly access all charm config as a mapping. |
212 | + conf = db.getrange('config', True) |
213 | + |
214 | + # Directly access all relation data as a mapping |
215 | + rels = db.getrange('rels', True) |
216 | + |
217 | + if __name__ == '__main__': |
218 | + with hook_data(): |
219 | + hook.execute() |
220 | + |
221 | + |
222 | +A more basic integration is via the hook_scope context manager which simply |
223 | +manages transaction scope (and records hook name, and timestamp):: |
224 | + |
225 | + >>> from unitdata import kv |
226 | + >>> db = kv() |
227 | + >>> with db.hook_scope('install'): |
228 | + ... # do work, in transactional scope. |
229 | + ... db.set('x', 1) |
230 | + >>> db.get('x') |
231 | + 1 |
232 | + |
233 | + |
234 | +Usage |
235 | +----- |
236 | + |
237 | +Values are automatically json de/serialized to preserve basic typing |
238 | +and complex data struct capabilities (dicts, lists, ints, booleans, etc). |
239 | + |
240 | +Individual values can be manipulated via get/set:: |
241 | + |
242 | + >>> kv.set('y', True) |
243 | + >>> kv.get('y') |
244 | + True |
245 | + |
246 | + # We can set complex values (dicts, lists) as a single key. |
247 | + >>> kv.set('config', {'a': 1, 'b': True'}) |
248 | + |
249 | + # Also supports returning dictionaries as a record which |
250 | + # provides attribute access. |
251 | + >>> config = kv.get('config', record=True) |
252 | + >>> config.b |
253 | + True |
254 | + |
255 | + |
256 | +Groups of keys can be manipulated with update/getrange:: |
257 | + |
258 | + >>> kv.update({'z': 1, 'y': 2}, prefix="gui.") |
259 | + >>> kv.getrange('gui.', strip=True) |
260 | + {'z': 1, 'y': 2} |
261 | + |
262 | +When updating values, its very helpful to understand which values |
263 | +have actually changed and how have they changed. The storage |
264 | +provides a delta method to provide for this:: |
265 | + |
266 | + >>> data = {'debug': True, 'option': 2} |
267 | + >>> delta = kv.delta(data, 'config.') |
268 | + >>> delta.debug.previous |
269 | + None |
270 | + >>> delta.debug.current |
271 | + True |
272 | + >>> delta |
273 | + {'debug': (None, True), 'option': (None, 2)} |
274 | + |
275 | +Note the delta method does not persist the actual change, it needs to |
276 | +be explicitly saved via 'update' method:: |
277 | + |
278 | + >>> kv.update(data, 'config.') |
279 | + |
280 | +Values modified in the context of a hook scope retain historical values |
281 | +associated to the hookname. |
282 | + |
283 | + >>> with db.hook_scope('config-changed'): |
284 | + ... db.set('x', 42) |
285 | + >>> db.gethistory('x') |
286 | + [(1, u'x', 1, u'install', u'2015-01-21T16:49:30.038372'), |
287 | + (2, u'x', 42, u'config-changed', u'2015-01-21T16:49:30.038786')] |
288 | + |
289 | +""" |
290 | + |
291 | +import collections |
292 | +import contextlib |
293 | +import datetime |
294 | +import json |
295 | +import os |
296 | +import pprint |
297 | +import sqlite3 |
298 | +import sys |
299 | + |
300 | +__author__ = 'Kapil Thangavelu <kapil.foss@gmail.com>' |
301 | + |
302 | + |
303 | +class Storage(object): |
304 | + """Simple key value database for local unit state within charms. |
305 | + |
306 | + Modifications are automatically committed at hook exit. That's |
307 | + currently regardless of exit code. |
308 | + |
309 | + To support dicts, lists, integer, floats, and booleans values |
310 | + are automatically json encoded/decoded. |
311 | + """ |
312 | + def __init__(self, path=None): |
313 | + self.db_path = path |
314 | + if path is None: |
315 | + self.db_path = os.path.join( |
316 | + os.environ.get('CHARM_DIR', ''), '.unit-state.db') |
317 | + self.conn = sqlite3.connect('%s' % self.db_path) |
318 | + self.cursor = self.conn.cursor() |
319 | + self.revision = None |
320 | + self._closed = False |
321 | + self._init() |
322 | + |
323 | + def close(self): |
324 | + if self._closed: |
325 | + return |
326 | + self.flush(False) |
327 | + self.cursor.close() |
328 | + self.conn.close() |
329 | + self._closed = True |
330 | + |
331 | + def _scoped_query(self, stmt, params=None): |
332 | + if params is None: |
333 | + params = [] |
334 | + return stmt, params |
335 | + |
336 | + def get(self, key, default=None, record=False): |
337 | + self.cursor.execute( |
338 | + *self._scoped_query( |
339 | + 'select data from kv where key=?', [key])) |
340 | + result = self.cursor.fetchone() |
341 | + if not result: |
342 | + return default |
343 | + if record: |
344 | + return Record(json.loads(result[0])) |
345 | + return json.loads(result[0]) |
346 | + |
347 | + def getrange(self, key_prefix, strip=False): |
348 | + stmt = "select key, data from kv where key like '%s%%'" % key_prefix |
349 | + self.cursor.execute(*self._scoped_query(stmt)) |
350 | + result = self.cursor.fetchall() |
351 | + |
352 | + if not result: |
353 | + return None |
354 | + if not strip: |
355 | + key_prefix = '' |
356 | + return dict([ |
357 | + (k[len(key_prefix):], json.loads(v)) for k, v in result]) |
358 | + |
359 | + def update(self, mapping, prefix=""): |
360 | + for k, v in mapping.items(): |
361 | + self.set("%s%s" % (prefix, k), v) |
362 | + |
363 | + def unset(self, key): |
364 | + self.cursor.execute('delete from kv where key=?', [key]) |
365 | + if self.revision and self.cursor.rowcount: |
366 | + self.cursor.execute( |
367 | + 'insert into kv_revisions values (?, ?, ?)', |
368 | + [key, self.revision, json.dumps('DELETED')]) |
369 | + |
370 | + def set(self, key, value): |
371 | + serialized = json.dumps(value) |
372 | + |
373 | + self.cursor.execute( |
374 | + 'select data from kv where key=?', [key]) |
375 | + exists = self.cursor.fetchone() |
376 | + |
377 | + # Skip mutations to the same value |
378 | + if exists: |
379 | + if exists[0] == serialized: |
380 | + return value |
381 | + |
382 | + if not exists: |
383 | + self.cursor.execute( |
384 | + 'insert into kv (key, data) values (?, ?)', |
385 | + (key, serialized)) |
386 | + else: |
387 | + self.cursor.execute(''' |
388 | + update kv |
389 | + set data = ? |
390 | + where key = ?''', [serialized, key]) |
391 | + |
392 | + # Save |
393 | + if not self.revision: |
394 | + return value |
395 | + |
396 | + self.cursor.execute( |
397 | + 'select 1 from kv_revisions where key=? and revision=?', |
398 | + [key, self.revision]) |
399 | + exists = self.cursor.fetchone() |
400 | + |
401 | + if not exists: |
402 | + self.cursor.execute( |
403 | + '''insert into kv_revisions ( |
404 | + revision, key, data) values (?, ?, ?)''', |
405 | + (self.revision, key, serialized)) |
406 | + else: |
407 | + self.cursor.execute( |
408 | + ''' |
409 | + update kv_revisions |
410 | + set data = ? |
411 | + where key = ? |
412 | + and revision = ?''', |
413 | + [serialized, key, self.revision]) |
414 | + |
415 | + return value |
416 | + |
417 | + def delta(self, mapping, prefix): |
418 | + """ |
419 | + return a delta containing values that have changed. |
420 | + """ |
421 | + previous = self.getrange(prefix, strip=True) |
422 | + if not previous: |
423 | + pk = set() |
424 | + else: |
425 | + pk = set(previous.keys()) |
426 | + ck = set(mapping.keys()) |
427 | + delta = DeltaSet() |
428 | + |
429 | + # added |
430 | + for k in ck.difference(pk): |
431 | + delta[k] = Delta(None, mapping[k]) |
432 | + |
433 | + # removed |
434 | + for k in pk.difference(ck): |
435 | + delta[k] = Delta(previous[k], None) |
436 | + |
437 | + # changed |
438 | + for k in pk.intersection(ck): |
439 | + c = mapping[k] |
440 | + p = previous[k] |
441 | + if c != p: |
442 | + delta[k] = Delta(p, c) |
443 | + |
444 | + return delta |
445 | + |
446 | + @contextlib.contextmanager |
447 | + def hook_scope(self, name=""): |
448 | + """Scope all future interactions to the current hook execution |
449 | + revision.""" |
450 | + assert not self.revision |
451 | + self.cursor.execute( |
452 | + 'insert into hooks (hook, date) values (?, ?)', |
453 | + (name or sys.argv[0], |
454 | + datetime.datetime.utcnow().isoformat())) |
455 | + self.revision = self.cursor.lastrowid |
456 | + try: |
457 | + yield self.revision |
458 | + self.revision = None |
459 | + except: |
460 | + self.flush(False) |
461 | + self.revision = None |
462 | + raise |
463 | + else: |
464 | + self.flush() |
465 | + |
466 | + def flush(self, save=True): |
467 | + if save: |
468 | + self.conn.commit() |
469 | + elif self._closed: |
470 | + return |
471 | + else: |
472 | + self.conn.rollback() |
473 | + |
474 | + def _init(self): |
475 | + self.cursor.execute(''' |
476 | + create table if not exists kv ( |
477 | + key text, |
478 | + data text, |
479 | + primary key (key) |
480 | + )''') |
481 | + self.cursor.execute(''' |
482 | + create table if not exists kv_revisions ( |
483 | + key text, |
484 | + revision integer, |
485 | + data text, |
486 | + primary key (key, revision) |
487 | + )''') |
488 | + self.cursor.execute(''' |
489 | + create table if not exists hooks ( |
490 | + version integer primary key autoincrement, |
491 | + hook text, |
492 | + date text |
493 | + )''') |
494 | + self.conn.commit() |
495 | + |
496 | + def gethistory(self, key, deserialize=False): |
497 | + self.cursor.execute( |
498 | + ''' |
499 | + select kv.revision, kv.key, kv.data, h.hook, h.date |
500 | + from kv_revisions kv, |
501 | + hooks h |
502 | + where kv.key=? |
503 | + and kv.revision = h.version |
504 | + ''', [key]) |
505 | + if deserialize is False: |
506 | + return self.cursor.fetchall() |
507 | + return map(_parse_history, self.cursor.fetchall()) |
508 | + |
509 | + def debug(self, fh=sys.stderr): |
510 | + self.cursor.execute('select * from kv') |
511 | + pprint.pprint(self.cursor.fetchall(), stream=fh) |
512 | + self.cursor.execute('select * from kv_revisions') |
513 | + pprint.pprint(self.cursor.fetchall(), stream=fh) |
514 | + |
515 | + |
516 | +def _parse_history(d): |
517 | + return (d[0], d[1], json.loads(d[2]), d[3], |
518 | + datetime.datetime.strptime(d[-1], "%Y-%m-%dT%H:%M:%S.%f")) |
519 | + |
520 | + |
521 | +class HookData(object): |
522 | + """Simple integration for existing hook exec frameworks. |
523 | + |
524 | + Records all unit information, and stores deltas for processing |
525 | + by the hook. |
526 | + |
527 | + Sample:: |
528 | + |
529 | + from charmhelper.core import hookenv, unitdata |
530 | + |
531 | + changes = unitdata.HookData() |
532 | + db = unitdata.kv() |
533 | + hooks = hookenv.Hooks() |
534 | + |
535 | + @hooks.hook |
536 | + def config_changed(): |
537 | + # View all changes to configuration |
538 | + for changed, (prev, cur) in changes.conf.items(): |
539 | + print('config changed', changed, |
540 | + 'previous value', prev, |
541 | + 'current value', cur) |
542 | + |
543 | + # Get some unit specific bookeeping |
544 | + if not db.get('pkg_key'): |
545 | + key = urllib.urlopen('https://example.com/pkg_key').read() |
546 | + db.set('pkg_key', key) |
547 | + |
548 | + if __name__ == '__main__': |
549 | + with changes(): |
550 | + hook.execute() |
551 | + |
552 | + """ |
553 | + def __init__(self): |
554 | + self.kv = kv() |
555 | + self.conf = None |
556 | + self.rels = None |
557 | + |
558 | + @contextlib.contextmanager |
559 | + def __call__(self): |
560 | + from charmhelpers.core import hookenv |
561 | + hook_name = hookenv.hook_name() |
562 | + |
563 | + with self.kv.hook_scope(hook_name): |
564 | + self._record_charm_version(hookenv.charm_dir()) |
565 | + delta_config, delta_relation = self._record_hook(hookenv) |
566 | + yield self.kv, delta_config, delta_relation |
567 | + |
568 | + def _record_charm_version(self, charm_dir): |
569 | + # Record revisions.. charm revisions are meaningless |
570 | + # to charm authors as they don't control the revision. |
571 | + # so logic dependnent on revision is not particularly |
572 | + # useful, however it is useful for debugging analysis. |
573 | + charm_rev = open( |
574 | + os.path.join(charm_dir, 'revision')).read().strip() |
575 | + charm_rev = charm_rev or '0' |
576 | + revs = self.kv.get('charm_revisions', []) |
577 | + if charm_rev not in revs: |
578 | + revs.append(charm_rev.strip() or '0') |
579 | + self.kv.set('charm_revisions', revs) |
580 | + |
581 | + def _record_hook(self, hookenv): |
582 | + data = hookenv.execution_environment() |
583 | + self.conf = conf_delta = self.kv.delta(data['conf'], 'config') |
584 | + self.rels = rels_delta = self.kv.delta(data['rels'], 'rels') |
585 | + self.kv.set('env', data['env']) |
586 | + self.kv.set('unit', data['unit']) |
587 | + self.kv.set('relid', data.get('relid')) |
588 | + return conf_delta, rels_delta |
589 | + |
590 | + |
591 | +class Record(dict): |
592 | + |
593 | + __slots__ = () |
594 | + |
595 | + def __getattr__(self, k): |
596 | + if k in self: |
597 | + return self[k] |
598 | + raise AttributeError(k) |
599 | + |
600 | + |
601 | +class DeltaSet(Record): |
602 | + |
603 | + __slots__ = () |
604 | + |
605 | + |
606 | +Delta = collections.namedtuple('Delta', ['previous', 'current']) |
607 | + |
608 | + |
609 | +_KV = None |
610 | + |
611 | + |
612 | +def kv(): |
613 | + global _KV |
614 | + if _KV is None: |
615 | + _KV = Storage() |
616 | + return _KV |
617 | |
618 | === modified file 'hooks/charmhelpers/fetch/archiveurl.py' |
619 | --- hooks/charmhelpers/fetch/archiveurl.py 2015-01-26 09:44:47 +0000 |
620 | +++ hooks/charmhelpers/fetch/archiveurl.py 2015-02-16 12:07:04 +0000 |
621 | @@ -18,6 +18,16 @@ |
622 | import hashlib |
623 | import re |
624 | |
625 | +from charmhelpers.fetch import ( |
626 | + BaseFetchHandler, |
627 | + UnhandledSource |
628 | +) |
629 | +from charmhelpers.payload.archive import ( |
630 | + get_archive_handler, |
631 | + extract, |
632 | +) |
633 | +from charmhelpers.core.host import mkdir, check_hash |
634 | + |
635 | import six |
636 | if six.PY3: |
637 | from urllib.request import ( |
638 | @@ -35,16 +45,6 @@ |
639 | ) |
640 | from urlparse import urlparse, urlunparse, parse_qs |
641 | |
642 | -from charmhelpers.fetch import ( |
643 | - BaseFetchHandler, |
644 | - UnhandledSource |
645 | -) |
646 | -from charmhelpers.payload.archive import ( |
647 | - get_archive_handler, |
648 | - extract, |
649 | -) |
650 | -from charmhelpers.core.host import mkdir, check_hash |
651 | - |
652 | |
653 | def splituser(host): |
654 | '''urllib.splituser(), but six's support of this seems broken''' |
655 | |
656 | === modified file 'hooks/charmhelpers/fetch/giturl.py' |
657 | --- hooks/charmhelpers/fetch/giturl.py 2015-01-26 09:44:47 +0000 |
658 | +++ hooks/charmhelpers/fetch/giturl.py 2015-02-16 12:07:04 +0000 |
659 | @@ -32,7 +32,7 @@ |
660 | apt_install("python-git") |
661 | from git import Repo |
662 | |
663 | -from git.exc import GitCommandError |
664 | +from git.exc import GitCommandError # noqa E402 |
665 | |
666 | |
667 | class GitUrlFetchHandler(BaseFetchHandler): |
668 | |
669 | === modified file 'hooks/keystone_context.py' |
670 | --- hooks/keystone_context.py 2015-01-27 23:56:15 +0000 |
671 | +++ hooks/keystone_context.py 2015-02-16 12:07:04 +0000 |
672 | @@ -21,6 +21,10 @@ |
673 | INFO, |
674 | ) |
675 | |
676 | +from charmhelpers.core.strutils import ( |
677 | + bool_from_string, |
678 | +) |
679 | + |
680 | from charmhelpers.contrib.hahelpers.apache import install_ca_cert |
681 | |
682 | CA_CERT_PATH = '/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt' |
683 | @@ -179,8 +183,12 @@ |
684 | singlenode_mode=True) |
685 | ctxt['public_port'] = determine_api_port(api_port('keystone-public'), |
686 | singlenode_mode=True) |
687 | - ctxt['debug'] = config('debug') in ['yes', 'true', 'True'] |
688 | - ctxt['verbose'] = config('verbose') in ['yes', 'true', 'True'] |
689 | + |
690 | + debug = config('debug') |
691 | + ctxt['debug'] = debug and bool_from_string(debug) |
692 | + verbose = config('verbose') |
693 | + ctxt['verbose'] = verbose and bool_from_string(verbose) |
694 | + |
695 | ctxt['identity_backend'] = config('identity-backend') |
696 | ctxt['assignment_backend'] = config('assignment-backend') |
697 | if config('identity-backend') == 'ldap': |
698 | @@ -194,7 +202,8 @@ |
699 | flags = context.config_flags_parser(ldap_flags) |
700 | ctxt['ldap_config_flags'] = flags |
701 | |
702 | - if config('enable-pki') not in ['false', 'False', 'no', 'No']: |
703 | + enable_pki = config('enable-pki') |
704 | + if enable_pki and bool_from_string(enable_pki): |
705 | ctxt['signing'] = True |
706 | |
707 | # Base endpoint URL's which are used in keystone responses |
708 | @@ -214,7 +223,7 @@ |
709 | def __call__(self): |
710 | ctxt = {} |
711 | debug = config('debug') |
712 | - if debug and debug.lower() in ['yes', 'true']: |
713 | + if debug and bool_from_string(debug): |
714 | ctxt['root_level'] = 'DEBUG' |
715 | |
716 | return ctxt |
717 | |
718 | === modified file 'hooks/keystone_hooks.py' |
719 | --- hooks/keystone_hooks.py 2015-02-06 12:05:52 +0000 |
720 | +++ hooks/keystone_hooks.py 2015-02-16 12:07:04 +0000 |
721 | @@ -33,6 +33,10 @@ |
722 | restart_on_change, |
723 | ) |
724 | |
725 | +from charmhelpers.core.strutils import ( |
726 | + bool_from_string, |
727 | +) |
728 | + |
729 | from charmhelpers.fetch import ( |
730 | apt_install, apt_update, |
731 | filter_installed_packages |
732 | @@ -65,7 +69,6 @@ |
733 | CA_CERT_PATH, |
734 | ensure_permissions, |
735 | get_ssl_sync_request_units, |
736 | - is_str_true, |
737 | is_ssl_cert_master, |
738 | is_db_ready, |
739 | clear_ssl_synced_units, |
740 | @@ -301,10 +304,10 @@ |
741 | """ |
742 | unit = local_unit().replace('/', '-') |
743 | count = 0 |
744 | - if is_str_true(config('use-https')): |
745 | + if bool_from_string(config('use-https')): |
746 | count += 1 |
747 | |
748 | - if is_str_true(config('https-service-endpoints')): |
749 | + if bool_from_string(config('https-service-endpoints')): |
750 | count += 2 |
751 | |
752 | if count: |
753 | |
754 | === modified file 'hooks/keystone_utils.py' |
755 | --- hooks/keystone_utils.py 2015-02-05 17:32:30 +0000 |
756 | +++ hooks/keystone_utils.py 2015-02-16 12:07:04 +0000 |
757 | @@ -48,6 +48,10 @@ |
758 | write_file, |
759 | ) |
760 | |
761 | +from charmhelpers.core.strutils import ( |
762 | + bool_from_string, |
763 | +) |
764 | + |
765 | import charmhelpers.contrib.unison as unison |
766 | |
767 | from charmhelpers.core.decorators import ( |
768 | @@ -226,13 +230,6 @@ |
769 | } |
770 | |
771 | |
772 | -def is_str_true(value): |
773 | - if value and value.lower() in ['true', 'yes']: |
774 | - return True |
775 | - |
776 | - return False |
777 | - |
778 | - |
779 | def resource_map(): |
780 | ''' |
781 | Dynamically generate a map of resources that will be managed for a single |
782 | @@ -823,8 +820,8 @@ |
783 | |
784 | def is_ssl_enabled(): |
785 | # Don't do anything if we are not in ssl/https mode |
786 | - if (is_str_true(config('use-https')) or |
787 | - is_str_true(config('https-service-endpoints'))): |
788 | + if (bool_from_string(config('use-https')) or |
789 | + bool_from_string(config('https-service-endpoints'))): |
790 | log("SSL/HTTPS is enabled", level=DEBUG) |
791 | return True |
792 | |
793 | @@ -906,13 +903,13 @@ |
794 | """ |
795 | paths_to_sync = [SYNC_FLAGS_DIR] |
796 | |
797 | - if is_str_true(config('https-service-endpoints')): |
798 | + if bool_from_string(config('https-service-endpoints')): |
799 | log("Syncing all endpoint certs since https-service-endpoints=True", |
800 | level=DEBUG) |
801 | paths_to_sync.append(SSL_DIR) |
802 | paths_to_sync.append(CA_CERT_PATH) |
803 | |
804 | - if is_str_true(config('use-https')): |
805 | + if bool_from_string(config('use-https')): |
806 | log("Syncing keystone-endpoint certs since use-https=True", |
807 | level=DEBUG) |
808 | paths_to_sync.append(SSL_DIR) |
809 | @@ -1150,7 +1147,7 @@ |
810 | relation_data["auth_port"] = config('admin-port') |
811 | relation_data["service_port"] = config('service-port') |
812 | relation_data["region"] = config('region') |
813 | - if is_str_true(config('https-service-endpoints')): |
814 | + if bool_from_string(config('https-service-endpoints')): |
815 | # Pass CA cert as client will need it to |
816 | # verify https connections |
817 | ca = get_ca(user=SSH_USER) |
818 | @@ -1290,7 +1287,7 @@ |
819 | relation_data["auth_protocol"] = "http" |
820 | relation_data["service_protocol"] = "http" |
821 | # generate or get a new cert/key for service if set to manage certs. |
822 | - if is_str_true(config('https-service-endpoints')): |
823 | + if bool_from_string(config('https-service-endpoints')): |
824 | ca = get_ca(user=SSH_USER) |
825 | # NOTE(jamespage) may have multiple cns to deal with to iterate |
826 | https_cns = set(https_cns) |
827 | |
828 | === modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py' |
829 | --- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-01-26 09:44:47 +0000 |
830 | +++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-02-16 12:07:04 +0000 |
831 | @@ -71,16 +71,19 @@ |
832 | services.append(this_service) |
833 | use_source = ['mysql', 'mongodb', 'rabbitmq-server', 'ceph', |
834 | 'ceph-osd', 'ceph-radosgw'] |
835 | + # Openstack subordinate charms do not expose an origin option as that |
836 | + # is controlled by the principle |
837 | + ignore = ['neutron-openvswitch'] |
838 | |
839 | if self.openstack: |
840 | for svc in services: |
841 | - if svc['name'] not in use_source: |
842 | + if svc['name'] not in use_source + ignore: |
843 | config = {'openstack-origin': self.openstack} |
844 | self.d.configure(svc['name'], config) |
845 | |
846 | if self.source: |
847 | for svc in services: |
848 | - if svc['name'] in use_source: |
849 | + if svc['name'] in use_source and svc['name'] not in ignore: |
850 | config = {'source': self.source} |
851 | self.d.configure(svc['name'], config) |
852 |
charm_lint_check #2030 keystone-next for hopem mp249801
LINT OK: passed
Build: http:// 10.245. 162.77: 8080/job/ charm_lint_ check/2030/