Merge lp:~tribaal/charms/trusty/keystone/update-stable-charm-helpers into lp:~openstack-charmers-archive/charms/trusty/keystone/trunk

Proposed by Chris Glass
Status: Rejected
Rejected by: Chris Glass
Proposed branch: lp:~tribaal/charms/trusty/keystone/update-stable-charm-helpers
Merge into: lp:~openstack-charmers-archive/charms/trusty/keystone/trunk
Diff against target: 534 lines (+257/-53)
10 files modified
hooks/charmhelpers/cli/__init__.py (+1/-5)
hooks/charmhelpers/cli/commands.py (+4/-4)
hooks/charmhelpers/contrib/network/ip.py (+5/-3)
hooks/charmhelpers/contrib/openstack/context.py (+8/-9)
hooks/charmhelpers/contrib/openstack/templating.py (+2/-1)
hooks/charmhelpers/contrib/openstack/utils.py (+7/-5)
hooks/charmhelpers/contrib/storage/linux/ceph.py (+224/-2)
hooks/charmhelpers/contrib/storage/linux/utils.py (+3/-2)
hooks/charmhelpers/core/hookenv.py (+1/-20)
hooks/charmhelpers/core/host.py (+2/-2)
To merge this branch: bzr merge lp:~tribaal/charms/trusty/keystone/update-stable-charm-helpers
Reviewer Review Type Date Requested Status
OpenStack Charmers Pending
Review via email: mp+271661@code.launchpad.net

Description of the change

This syncs charm-helpers from lp:~openstack-charmers/charm-helpers/stable (using "make sync").

To post a comment you must log in.
Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #10249 keystone for tribaal mp271661
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/10249/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #9405 keystone for tribaal mp271661
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/9405/

156. By Chris Glass

Updated charm-helpers again.

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #6491 keystone for tribaal mp271661
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12449164/
Build: http://10.245.162.77:8080/job/charm_amulet_test/6491/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_lint_check #10251 keystone for tribaal mp271661
    LINT OK: passed

Build: http://10.245.162.77:8080/job/charm_lint_check/10251/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_unit_test #9406 keystone for tribaal mp271661
    UNIT OK: passed

Build: http://10.245.162.77:8080/job/charm_unit_test/9406/

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote :

charm_amulet_test #6493 keystone for tribaal mp271661
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
make: *** [functional_test] Error 1
ERROR:root:Make target returned non-zero.

Full amulet test output: http://paste.ubuntu.com/12449437/
Build: http://10.245.162.77:8080/job/charm_amulet_test/6493/

Unmerged revisions

156. By Chris Glass

Updated charm-helpers again.

155. By Chris Glass

Updated charm-helpers from lp:~openstack-charmers/charm-helpers/stable

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hooks/charmhelpers/cli/__init__.py'
2--- hooks/charmhelpers/cli/__init__.py 2015-08-10 16:35:40 +0000
3+++ hooks/charmhelpers/cli/__init__.py 2015-09-18 14:18:53 +0000
4@@ -152,15 +152,11 @@
5 arguments = self.argument_parser.parse_args()
6 argspec = inspect.getargspec(arguments.func)
7 vargs = []
8- kwargs = {}
9 for arg in argspec.args:
10 vargs.append(getattr(arguments, arg))
11 if argspec.varargs:
12 vargs.extend(getattr(arguments, argspec.varargs))
13- if argspec.keywords:
14- for kwarg in argspec.keywords.items():
15- kwargs[kwarg] = getattr(arguments, kwarg)
16- output = arguments.func(*vargs, **kwargs)
17+ output = arguments.func(*vargs)
18 if getattr(arguments.func, '_cli_test_command', False):
19 self.exit_code = 0 if output else 1
20 output = ''
21
22=== modified file 'hooks/charmhelpers/cli/commands.py'
23--- hooks/charmhelpers/cli/commands.py 2015-08-10 16:35:40 +0000
24+++ hooks/charmhelpers/cli/commands.py 2015-09-18 14:18:53 +0000
25@@ -26,7 +26,7 @@
26 """
27 Import the sub-modules which have decorated subcommands to register with chlp.
28 """
29-import host # noqa
30-import benchmark # noqa
31-import unitdata # noqa
32-from charmhelpers.core import hookenv # noqa
33+from . import host # noqa
34+from . import benchmark # noqa
35+from . import unitdata # noqa
36+from . import hookenv # noqa
37
38=== modified file 'hooks/charmhelpers/contrib/network/ip.py'
39--- hooks/charmhelpers/contrib/network/ip.py 2015-03-11 11:45:09 +0000
40+++ hooks/charmhelpers/contrib/network/ip.py 2015-09-18 14:18:53 +0000
41@@ -23,7 +23,7 @@
42 from functools import partial
43
44 from charmhelpers.core.hookenv import unit_get
45-from charmhelpers.fetch import apt_install
46+from charmhelpers.fetch import apt_install, apt_update
47 from charmhelpers.core.hookenv import (
48 log,
49 WARNING,
50@@ -32,13 +32,15 @@
51 try:
52 import netifaces
53 except ImportError:
54- apt_install('python-netifaces')
55+ apt_update(fatal=True)
56+ apt_install('python-netifaces', fatal=True)
57 import netifaces
58
59 try:
60 import netaddr
61 except ImportError:
62- apt_install('python-netaddr')
63+ apt_update(fatal=True)
64+ apt_install('python-netaddr', fatal=True)
65 import netaddr
66
67
68
69=== modified file 'hooks/charmhelpers/contrib/openstack/context.py'
70--- hooks/charmhelpers/contrib/openstack/context.py 2015-08-10 16:35:40 +0000
71+++ hooks/charmhelpers/contrib/openstack/context.py 2015-09-18 14:18:53 +0000
72@@ -483,13 +483,15 @@
73
74 log('Generating template context for ceph', level=DEBUG)
75 mon_hosts = []
76- auth = None
77- key = None
78- use_syslog = str(config('use-syslog')).lower()
79+ ctxt = {
80+ 'use_syslog': str(config('use-syslog')).lower()
81+ }
82 for rid in relation_ids('ceph'):
83 for unit in related_units(rid):
84- auth = relation_get('auth', rid=rid, unit=unit)
85- key = relation_get('key', rid=rid, unit=unit)
86+ if not ctxt.get('auth'):
87+ ctxt['auth'] = relation_get('auth', rid=rid, unit=unit)
88+ if not ctxt.get('key'):
89+ ctxt['key'] = relation_get('key', rid=rid, unit=unit)
90 ceph_pub_addr = relation_get('ceph-public-address', rid=rid,
91 unit=unit)
92 unit_priv_addr = relation_get('private-address', rid=rid,
93@@ -498,10 +500,7 @@
94 ceph_addr = format_ipv6_addr(ceph_addr) or ceph_addr
95 mon_hosts.append(ceph_addr)
96
97- ctxt = {'mon_hosts': ' '.join(sorted(mon_hosts)),
98- 'auth': auth,
99- 'key': key,
100- 'use_syslog': use_syslog}
101+ ctxt['mon_hosts'] = ' '.join(sorted(mon_hosts))
102
103 if not os.path.isdir('/etc/ceph'):
104 os.mkdir('/etc/ceph')
105
106=== modified file 'hooks/charmhelpers/contrib/openstack/templating.py'
107--- hooks/charmhelpers/contrib/openstack/templating.py 2015-08-10 16:35:40 +0000
108+++ hooks/charmhelpers/contrib/openstack/templating.py 2015-09-18 14:18:53 +0000
109@@ -18,7 +18,7 @@
110
111 import six
112
113-from charmhelpers.fetch import apt_install
114+from charmhelpers.fetch import apt_install, apt_update
115 from charmhelpers.core.hookenv import (
116 log,
117 ERROR,
118@@ -29,6 +29,7 @@
119 try:
120 from jinja2 import FileSystemLoader, ChoiceLoader, Environment, exceptions
121 except ImportError:
122+ apt_update(fatal=True)
123 apt_install('python-jinja2', fatal=True)
124 from jinja2 import FileSystemLoader, ChoiceLoader, Environment, exceptions
125
126
127=== modified file 'hooks/charmhelpers/contrib/openstack/utils.py'
128--- hooks/charmhelpers/contrib/openstack/utils.py 2015-08-10 16:35:40 +0000
129+++ hooks/charmhelpers/contrib/openstack/utils.py 2015-09-18 14:18:53 +0000
130@@ -1,5 +1,3 @@
131-#!/usr/bin/python
132-
133 # Copyright 2014-2015 Canonical Limited.
134 #
135 # This file is part of charm-helpers.
136@@ -167,9 +165,9 @@
137 error_out(e)
138
139
140-def get_os_version_codename(codename):
141+def get_os_version_codename(codename, version_map=OPENSTACK_CODENAMES):
142 '''Determine OpenStack version number from codename.'''
143- for k, v in six.iteritems(OPENSTACK_CODENAMES):
144+ for k, v in six.iteritems(version_map):
145 if v == codename:
146 return k
147 e = 'Could not derive OpenStack version for '\
148@@ -392,7 +390,11 @@
149 import apt_pkg as apt
150 src = config('openstack-origin')
151 cur_vers = get_os_version_package(package)
152- available_vers = get_os_version_install_source(src)
153+ if "swift" in package:
154+ codename = get_os_codename_install_source(src)
155+ available_vers = get_os_version_codename(codename, SWIFT_CODENAMES)
156+ else:
157+ available_vers = get_os_version_install_source(src)
158 apt.init()
159 return apt.version_compare(available_vers, cur_vers) == 1
160
161
162=== modified file 'hooks/charmhelpers/contrib/storage/linux/ceph.py'
163--- hooks/charmhelpers/contrib/storage/linux/ceph.py 2015-08-10 16:35:40 +0000
164+++ hooks/charmhelpers/contrib/storage/linux/ceph.py 2015-09-18 14:18:53 +0000
165@@ -28,6 +28,7 @@
166 import shutil
167 import json
168 import time
169+import uuid
170
171 from subprocess import (
172 check_call,
173@@ -35,8 +36,10 @@
174 CalledProcessError,
175 )
176 from charmhelpers.core.hookenv import (
177+ local_unit,
178 relation_get,
179 relation_ids,
180+ relation_set,
181 related_units,
182 log,
183 DEBUG,
184@@ -411,17 +414,52 @@
185
186 The API is versioned and defaults to version 1.
187 """
188- def __init__(self, api_version=1):
189+ def __init__(self, api_version=1, request_id=None):
190 self.api_version = api_version
191+ if request_id:
192+ self.request_id = request_id
193+ else:
194+ self.request_id = str(uuid.uuid1())
195 self.ops = []
196
197 def add_op_create_pool(self, name, replica_count=3):
198 self.ops.append({'op': 'create-pool', 'name': name,
199 'replicas': replica_count})
200
201+ def set_ops(self, ops):
202+ """Set request ops to provided value.
203+
204+ Useful for injecting ops that come from a previous request
205+ to allow comparisons to ensure validity.
206+ """
207+ self.ops = ops
208+
209 @property
210 def request(self):
211- return json.dumps({'api-version': self.api_version, 'ops': self.ops})
212+ return json.dumps({'api-version': self.api_version, 'ops': self.ops,
213+ 'request-id': self.request_id})
214+
215+ def _ops_equal(self, other):
216+ if len(self.ops) == len(other.ops):
217+ for req_no in range(0, len(self.ops)):
218+ for key in ['replicas', 'name', 'op']:
219+ if self.ops[req_no][key] != other.ops[req_no][key]:
220+ return False
221+ else:
222+ return False
223+ return True
224+
225+ def __eq__(self, other):
226+ if not isinstance(other, self.__class__):
227+ return False
228+ if self.api_version == other.api_version and \
229+ self._ops_equal(other):
230+ return True
231+ else:
232+ return False
233+
234+ def __ne__(self, other):
235+ return not self.__eq__(other)
236
237
238 class CephBrokerRsp(object):
239@@ -431,14 +469,198 @@
240
241 The API is versioned and defaults to version 1.
242 """
243+
244 def __init__(self, encoded_rsp):
245 self.api_version = None
246 self.rsp = json.loads(encoded_rsp)
247
248 @property
249+ def request_id(self):
250+ return self.rsp.get('request-id')
251+
252+ @property
253 def exit_code(self):
254 return self.rsp.get('exit-code')
255
256 @property
257 def exit_msg(self):
258 return self.rsp.get('stderr')
259+
260+
261+# Ceph Broker Conversation:
262+# If a charm needs an action to be taken by ceph it can create a CephBrokerRq
263+# and send that request to ceph via the ceph relation. The CephBrokerRq has a
264+# unique id so that the client can identity which CephBrokerRsp is associated
265+# with the request. Ceph will also respond to each client unit individually
266+# creating a response key per client unit eg glance/0 will get a CephBrokerRsp
267+# via key broker-rsp-glance-0
268+#
269+# To use this the charm can just do something like:
270+#
271+# from charmhelpers.contrib.storage.linux.ceph import (
272+# send_request_if_needed,
273+# is_request_complete,
274+# CephBrokerRq,
275+# )
276+#
277+# @hooks.hook('ceph-relation-changed')
278+# def ceph_changed():
279+# rq = CephBrokerRq()
280+# rq.add_op_create_pool(name='poolname', replica_count=3)
281+#
282+# if is_request_complete(rq):
283+# <Request complete actions>
284+# else:
285+# send_request_if_needed(get_ceph_request())
286+#
287+# CephBrokerRq and CephBrokerRsp are serialized into JSON. Below is an example
288+# of glance having sent a request to ceph which ceph has successfully processed
289+# 'ceph:8': {
290+# 'ceph/0': {
291+# 'auth': 'cephx',
292+# 'broker-rsp-glance-0': '{"request-id": "0bc7dc54", "exit-code": 0}',
293+# 'broker_rsp': '{"request-id": "0da543b8", "exit-code": 0}',
294+# 'ceph-public-address': '10.5.44.103',
295+# 'key': 'AQCLDttVuHXINhAAvI144CB09dYchhHyTUY9BQ==',
296+# 'private-address': '10.5.44.103',
297+# },
298+# 'glance/0': {
299+# 'broker_req': ('{"api-version": 1, "request-id": "0bc7dc54", '
300+# '"ops": [{"replicas": 3, "name": "glance", '
301+# '"op": "create-pool"}]}'),
302+# 'private-address': '10.5.44.109',
303+# },
304+# }
305+
306+def get_previous_request(rid):
307+ """Return the last ceph broker request sent on a given relation
308+
309+ @param rid: Relation id to query for request
310+ """
311+ request = None
312+ broker_req = relation_get(attribute='broker_req', rid=rid,
313+ unit=local_unit())
314+ if broker_req:
315+ request_data = json.loads(broker_req)
316+ request = CephBrokerRq(api_version=request_data['api-version'],
317+ request_id=request_data['request-id'])
318+ request.set_ops(request_data['ops'])
319+
320+ return request
321+
322+
323+def get_request_states(request):
324+ """Return a dict of requests per relation id with their corresponding
325+ completion state.
326+
327+ This allows a charm, which has a request for ceph, to see whether there is
328+ an equivalent request already being processed and if so what state that
329+ request is in.
330+
331+ @param request: A CephBrokerRq object
332+ """
333+ complete = []
334+ requests = {}
335+ for rid in relation_ids('ceph'):
336+ complete = False
337+ previous_request = get_previous_request(rid)
338+ if request == previous_request:
339+ sent = True
340+ complete = is_request_complete_for_rid(previous_request, rid)
341+ else:
342+ sent = False
343+ complete = False
344+
345+ requests[rid] = {
346+ 'sent': sent,
347+ 'complete': complete,
348+ }
349+
350+ return requests
351+
352+
353+def is_request_sent(request):
354+ """Check to see if a functionally equivalent request has already been sent
355+
356+ Returns True if a similair request has been sent
357+
358+ @param request: A CephBrokerRq object
359+ """
360+ states = get_request_states(request)
361+ for rid in states.keys():
362+ if not states[rid]['sent']:
363+ return False
364+
365+ return True
366+
367+
368+def is_request_complete(request):
369+ """Check to see if a functionally equivalent request has already been
370+ completed
371+
372+ Returns True if a similair request has been completed
373+
374+ @param request: A CephBrokerRq object
375+ """
376+ states = get_request_states(request)
377+ for rid in states.keys():
378+ if not states[rid]['complete']:
379+ return False
380+
381+ return True
382+
383+
384+def is_request_complete_for_rid(request, rid):
385+ """Check if a given request has been completed on the given relation
386+
387+ @param request: A CephBrokerRq object
388+ @param rid: Relation ID
389+ """
390+ broker_key = get_broker_rsp_key()
391+ for unit in related_units(rid):
392+ rdata = relation_get(rid=rid, unit=unit)
393+ if rdata.get(broker_key):
394+ rsp = CephBrokerRsp(rdata.get(broker_key))
395+ if rsp.request_id == request.request_id:
396+ if not rsp.exit_code:
397+ return True
398+ else:
399+ # The remote unit sent no reply targeted at this unit so either the
400+ # remote ceph cluster does not support unit targeted replies or it
401+ # has not processed our request yet.
402+ if rdata.get('broker_rsp'):
403+ request_data = json.loads(rdata['broker_rsp'])
404+ if request_data.get('request-id'):
405+ log('Ignoring legacy broker_rsp without unit key as remote '
406+ 'service supports unit specific replies', level=DEBUG)
407+ else:
408+ log('Using legacy broker_rsp as remote service does not '
409+ 'supports unit specific replies', level=DEBUG)
410+ rsp = CephBrokerRsp(rdata['broker_rsp'])
411+ if not rsp.exit_code:
412+ return True
413+
414+ return False
415+
416+
417+def get_broker_rsp_key():
418+ """Return broker response key for this unit
419+
420+ This is the key that ceph is going to use to pass request status
421+ information back to this unit
422+ """
423+ return 'broker-rsp-' + local_unit().replace('/', '-')
424+
425+
426+def send_request_if_needed(request):
427+ """Send broker request if an equivalent request has not already been sent
428+
429+ @param request: A CephBrokerRq object
430+ """
431+ if is_request_sent(request):
432+ log('Request already sent but not complete, not sending new request',
433+ level=DEBUG)
434+ else:
435+ for rid in relation_ids('ceph'):
436+ log('Sending request {}'.format(request.request_id), level=DEBUG)
437+ relation_set(relation_id=rid, broker_req=request.request)
438
439=== modified file 'hooks/charmhelpers/contrib/storage/linux/utils.py'
440--- hooks/charmhelpers/contrib/storage/linux/utils.py 2015-08-10 16:35:40 +0000
441+++ hooks/charmhelpers/contrib/storage/linux/utils.py 2015-09-18 14:18:53 +0000
442@@ -43,9 +43,10 @@
443
444 :param block_device: str: Full path of block device to clean.
445 '''
446+ # https://github.com/ceph/ceph/commit/fdd7f8d83afa25c4e09aaedd90ab93f3b64a677b
447 # sometimes sgdisk exits non-zero; this is OK, dd will clean up
448- call(['sgdisk', '--zap-all', '--mbrtogpt',
449- '--clear', block_device])
450+ call(['sgdisk', '--zap-all', '--', block_device])
451+ call(['sgdisk', '--clear', '--mbrtogpt', '--', block_device])
452 dev_end = check_output(['blockdev', '--getsz',
453 block_device]).decode('UTF-8')
454 gpt_end = int(dev_end.split()[0]) - 100
455
456=== modified file 'hooks/charmhelpers/core/hookenv.py'
457--- hooks/charmhelpers/core/hookenv.py 2015-08-10 16:35:40 +0000
458+++ hooks/charmhelpers/core/hookenv.py 2015-09-18 14:18:53 +0000
459@@ -34,23 +34,6 @@
460 import tempfile
461 from subprocess import CalledProcessError
462
463-try:
464- from charmhelpers.cli import cmdline
465-except ImportError as e:
466- # due to the anti-pattern of partially synching charmhelpers directly
467- # into charms, it's possible that charmhelpers.cli is not available;
468- # if that's the case, they don't really care about using the cli anyway,
469- # so mock it out
470- if str(e) == 'No module named cli':
471- class cmdline(object):
472- @classmethod
473- def subcommand(cls, *args, **kwargs):
474- def _wrap(func):
475- return func
476- return _wrap
477- else:
478- raise
479-
480 import six
481 if not six.PY3:
482 from UserDict import UserDict
483@@ -91,6 +74,7 @@
484 res = func(*args, **kwargs)
485 cache[key] = res
486 return res
487+ wrapper._wrapped = func
488 return wrapper
489
490
491@@ -190,7 +174,6 @@
492 return os.environ.get('JUJU_RELATION', None)
493
494
495-@cmdline.subcommand()
496 @cached
497 def relation_id(relation_name=None, service_or_unit=None):
498 """The relation ID for the current or a specified relation"""
499@@ -216,13 +199,11 @@
500 return os.environ.get('JUJU_REMOTE_UNIT', None)
501
502
503-@cmdline.subcommand()
504 def service_name():
505 """The name service group this unit belongs to"""
506 return local_unit().split('/')[0]
507
508
509-@cmdline.subcommand()
510 @cached
511 def remote_service_name(relid=None):
512 """The remote service name for a given relation-id (or the current relation)"""
513
514=== modified file 'hooks/charmhelpers/core/host.py'
515--- hooks/charmhelpers/core/host.py 2015-08-10 16:35:40 +0000
516+++ hooks/charmhelpers/core/host.py 2015-09-18 14:18:53 +0000
517@@ -72,7 +72,7 @@
518 stopped = service_stop(service_name)
519 # XXX: Support systemd too
520 override_path = os.path.join(
521- init_dir, '{}.conf.override'.format(service_name))
522+ init_dir, '{}.override'.format(service_name))
523 with open(override_path, 'w') as fh:
524 fh.write("manual\n")
525 return stopped
526@@ -86,7 +86,7 @@
527 if init_dir is None:
528 init_dir = "/etc/init"
529 override_path = os.path.join(
530- init_dir, '{}.conf.override'.format(service_name))
531+ init_dir, '{}.override'.format(service_name))
532 if os.path.exists(override_path):
533 os.unlink(override_path)
534 started = service_start(service_name)

Subscribers

People subscribed via source and target branches