Merge lp:~le-charmers/charms/trusty/glance/leadership-election into lp:~openstack-charmers-archive/charms/trusty/glance/next
- Trusty Tahr (14.04)
- leadership-election
- Merge into next
Proposed by
Edward Hope-Morley
Status: | Merged |
---|---|
Merged at revision: | 118 |
Proposed branch: | lp:~le-charmers/charms/trusty/glance/leadership-election |
Merge into: | lp:~openstack-charmers-archive/charms/trusty/glance/next |
Diff against target: |
590 lines (+246/-39) 10 files modified
hooks/charmhelpers/contrib/hahelpers/cluster.py (+37/-2) hooks/charmhelpers/contrib/openstack/neutron.py (+10/-5) hooks/charmhelpers/core/hookenv.py (+147/-10) hooks/charmhelpers/core/host.py (+1/-1) hooks/charmhelpers/core/services/base.py (+32/-11) hooks/charmhelpers/fetch/__init__.py (+1/-1) hooks/glance_relations.py (+3/-3) hooks/glance_utils.py (+9/-2) unit_tests/test_glance_relations.py (+3/-1) unit_tests/test_glance_utils.py (+3/-3) |
To merge this branch: | bzr merge lp:~le-charmers/charms/trusty/glance/leadership-election |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OpenStack Charmers | Pending | ||
Review via email: mp+255008@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 113. By Liam Young
-
Merged trunk in + LE charmhelper sync
- 114. By Liam Young
-
Fix lint and unit tests
- 115. By Liam Young
-
Resync le charm helpers
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'hooks/charmhelpers/contrib/hahelpers/cluster.py' |
2 | --- hooks/charmhelpers/contrib/hahelpers/cluster.py 2015-03-20 17:15:02 +0000 |
3 | +++ hooks/charmhelpers/contrib/hahelpers/cluster.py 2015-06-04 08:44:51 +0000 |
4 | @@ -44,6 +44,7 @@ |
5 | ERROR, |
6 | WARNING, |
7 | unit_get, |
8 | + is_leader as juju_is_leader |
9 | ) |
10 | from charmhelpers.core.decorators import ( |
11 | retry_on_exception, |
12 | @@ -52,6 +53,8 @@ |
13 | bool_from_string, |
14 | ) |
15 | |
16 | +DC_RESOURCE_NAME = 'DC' |
17 | + |
18 | |
19 | class HAIncompleteConfig(Exception): |
20 | pass |
21 | @@ -66,12 +69,21 @@ |
22 | Returns True if the charm executing this is the elected cluster leader. |
23 | |
24 | It relies on two mechanisms to determine leadership: |
25 | - 1. If the charm is part of a corosync cluster, call corosync to |
26 | + 1. If juju is sufficiently new and leadership election is supported, |
27 | + the is_leader command will be used. |
28 | + 2. If the charm is part of a corosync cluster, call corosync to |
29 | determine leadership. |
30 | - 2. If the charm is not part of a corosync cluster, the leader is |
31 | + 3. If the charm is not part of a corosync cluster, the leader is |
32 | determined as being "the alive unit with the lowest unit numer". In |
33 | other words, the oldest surviving unit. |
34 | """ |
35 | + try: |
36 | + return juju_is_leader() |
37 | + except NotImplementedError: |
38 | + log('Juju leadership election feature not enabled' |
39 | + ', using fallback support', |
40 | + level=WARNING) |
41 | + |
42 | if is_clustered(): |
43 | if not is_crm_leader(resource): |
44 | log('Deferring action to CRM leader.', level=INFO) |
45 | @@ -95,6 +107,27 @@ |
46 | return False |
47 | |
48 | |
49 | +def is_crm_dc(): |
50 | + """ |
51 | + Determine leadership by querying the pacemaker Designated Controller |
52 | + """ |
53 | + cmd = ['crm', 'status'] |
54 | + try: |
55 | + status = subprocess.check_output(cmd, stderr=subprocess.STDOUT) |
56 | + if not isinstance(status, six.text_type): |
57 | + status = six.text_type(status, "utf-8") |
58 | + except subprocess.CalledProcessError: |
59 | + return False |
60 | + current_dc = '' |
61 | + for line in status.split('\n'): |
62 | + if line.startswith('Current DC'): |
63 | + # Current DC: juju-lytrusty-machine-2 (168108163) - partition with quorum |
64 | + current_dc = line.split(':')[1].split()[0] |
65 | + if current_dc == get_unit_hostname(): |
66 | + return True |
67 | + return False |
68 | + |
69 | + |
70 | @retry_on_exception(5, base_delay=2, exc_type=CRMResourceNotFound) |
71 | def is_crm_leader(resource, retry=False): |
72 | """ |
73 | @@ -104,6 +137,8 @@ |
74 | We allow this operation to be retried to avoid the possibility of getting a |
75 | false negative. See LP #1396246 for more info. |
76 | """ |
77 | + if resource == DC_RESOURCE_NAME: |
78 | + return is_crm_dc() |
79 | cmd = ['crm', 'resource', 'show', resource] |
80 | try: |
81 | status = subprocess.check_output(cmd, stderr=subprocess.STDOUT) |
82 | |
83 | === modified file 'hooks/charmhelpers/contrib/openstack/neutron.py' |
84 | --- hooks/charmhelpers/contrib/openstack/neutron.py 2015-04-16 19:53:49 +0000 |
85 | +++ hooks/charmhelpers/contrib/openstack/neutron.py 2015-06-04 08:44:51 +0000 |
86 | @@ -256,11 +256,14 @@ |
87 | def parse_mappings(mappings): |
88 | parsed = {} |
89 | if mappings: |
90 | - mappings = mappings.split(' ') |
91 | + mappings = mappings.split() |
92 | for m in mappings: |
93 | p = m.partition(':') |
94 | - if p[1] == ':': |
95 | - parsed[p[0].strip()] = p[2].strip() |
96 | + key = p[0].strip() |
97 | + if p[1]: |
98 | + parsed[key] = p[2].strip() |
99 | + else: |
100 | + parsed[key] = '' |
101 | |
102 | return parsed |
103 | |
104 | @@ -283,13 +286,13 @@ |
105 | Returns dict of the form {bridge:port}. |
106 | """ |
107 | _mappings = parse_mappings(mappings) |
108 | - if not _mappings: |
109 | + if not _mappings or list(_mappings.values()) == ['']: |
110 | if not mappings: |
111 | return {} |
112 | |
113 | # For backwards-compatibility we need to support port-only provided in |
114 | # config. |
115 | - _mappings = {default_bridge: mappings.split(' ')[0]} |
116 | + _mappings = {default_bridge: mappings.split()[0]} |
117 | |
118 | bridges = _mappings.keys() |
119 | ports = _mappings.values() |
120 | @@ -309,6 +312,8 @@ |
121 | |
122 | Mappings must be a space-delimited list of provider:start:end mappings. |
123 | |
124 | + The start:end range is optional and may be omitted. |
125 | + |
126 | Returns dict of the form {provider: (start, end)}. |
127 | """ |
128 | _mappings = parse_mappings(mappings) |
129 | |
130 | === modified file 'hooks/charmhelpers/core/hookenv.py' |
131 | --- hooks/charmhelpers/core/hookenv.py 2015-04-16 19:53:49 +0000 |
132 | +++ hooks/charmhelpers/core/hookenv.py 2015-06-04 08:44:51 +0000 |
133 | @@ -21,12 +21,14 @@ |
134 | # Charm Helpers Developers <juju@lists.ubuntu.com> |
135 | |
136 | from __future__ import print_function |
137 | +from functools import wraps |
138 | import os |
139 | import json |
140 | import yaml |
141 | import subprocess |
142 | import sys |
143 | import errno |
144 | +import tempfile |
145 | from subprocess import CalledProcessError |
146 | |
147 | import six |
148 | @@ -58,15 +60,17 @@ |
149 | |
150 | will cache the result of unit_get + 'test' for future calls. |
151 | """ |
152 | + @wraps(func) |
153 | def wrapper(*args, **kwargs): |
154 | global cache |
155 | key = str((func, args, kwargs)) |
156 | try: |
157 | return cache[key] |
158 | except KeyError: |
159 | - res = func(*args, **kwargs) |
160 | - cache[key] = res |
161 | - return res |
162 | + pass # Drop out of the exception handler scope. |
163 | + res = func(*args, **kwargs) |
164 | + cache[key] = res |
165 | + return res |
166 | return wrapper |
167 | |
168 | |
169 | @@ -178,7 +182,7 @@ |
170 | |
171 | def remote_unit(): |
172 | """The remote unit for the current relation hook""" |
173 | - return os.environ['JUJU_REMOTE_UNIT'] |
174 | + return os.environ.get('JUJU_REMOTE_UNIT', None) |
175 | |
176 | |
177 | def service_name(): |
178 | @@ -250,6 +254,12 @@ |
179 | except KeyError: |
180 | return (self._prev_dict or {})[key] |
181 | |
182 | + def get(self, key, default=None): |
183 | + try: |
184 | + return self[key] |
185 | + except KeyError: |
186 | + return default |
187 | + |
188 | def keys(self): |
189 | prev_keys = [] |
190 | if self._prev_dict is not None: |
191 | @@ -353,18 +363,49 @@ |
192 | """Set relation information for the current unit""" |
193 | relation_settings = relation_settings if relation_settings else {} |
194 | relation_cmd_line = ['relation-set'] |
195 | + accepts_file = "--file" in subprocess.check_output( |
196 | + relation_cmd_line + ["--help"], universal_newlines=True) |
197 | if relation_id is not None: |
198 | relation_cmd_line.extend(('-r', relation_id)) |
199 | - for k, v in (list(relation_settings.items()) + list(kwargs.items())): |
200 | - if v is None: |
201 | - relation_cmd_line.append('{}='.format(k)) |
202 | - else: |
203 | - relation_cmd_line.append('{}={}'.format(k, v)) |
204 | - subprocess.check_call(relation_cmd_line) |
205 | + settings = relation_settings.copy() |
206 | + settings.update(kwargs) |
207 | + for key, value in settings.items(): |
208 | + # Force value to be a string: it always should, but some call |
209 | + # sites pass in things like dicts or numbers. |
210 | + if value is not None: |
211 | + settings[key] = "{}".format(value) |
212 | + if accepts_file: |
213 | + # --file was introduced in Juju 1.23.2. Use it by default if |
214 | + # available, since otherwise we'll break if the relation data is |
215 | + # too big. Ideally we should tell relation-set to read the data from |
216 | + # stdin, but that feature is broken in 1.23.2: Bug #1454678. |
217 | + with tempfile.NamedTemporaryFile(delete=False) as settings_file: |
218 | + settings_file.write(yaml.safe_dump(settings).encode("utf-8")) |
219 | + subprocess.check_call( |
220 | + relation_cmd_line + ["--file", settings_file.name]) |
221 | + os.remove(settings_file.name) |
222 | + else: |
223 | + for key, value in settings.items(): |
224 | + if value is None: |
225 | + relation_cmd_line.append('{}='.format(key)) |
226 | + else: |
227 | + relation_cmd_line.append('{}={}'.format(key, value)) |
228 | + subprocess.check_call(relation_cmd_line) |
229 | # Flush cache of any relation-gets for local unit |
230 | flush(local_unit()) |
231 | |
232 | |
233 | +def relation_clear(r_id=None): |
234 | + ''' Clears any relation data already set on relation r_id ''' |
235 | + settings = relation_get(rid=r_id, |
236 | + unit=local_unit()) |
237 | + for setting in settings: |
238 | + if setting not in ['public-address', 'private-address']: |
239 | + settings[setting] = None |
240 | + relation_set(relation_id=r_id, |
241 | + **settings) |
242 | + |
243 | + |
244 | @cached |
245 | def relation_ids(reltype=None): |
246 | """A list of relation_ids""" |
247 | @@ -509,6 +550,11 @@ |
248 | return None |
249 | |
250 | |
251 | +def unit_public_ip(): |
252 | + """Get this unit's public IP address""" |
253 | + return unit_get('public-address') |
254 | + |
255 | + |
256 | def unit_private_ip(): |
257 | """Get this unit's private IP address""" |
258 | return unit_get('private-address') |
259 | @@ -605,3 +651,94 @@ |
260 | |
261 | The results set by action_set are preserved.""" |
262 | subprocess.check_call(['action-fail', message]) |
263 | + |
264 | + |
265 | +def status_set(workload_state, message): |
266 | + """Set the workload state with a message |
267 | + |
268 | + Use status-set to set the workload state with a message which is visible |
269 | + to the user via juju status. If the status-set command is not found then |
270 | + assume this is juju < 1.23 and juju-log the message unstead. |
271 | + |
272 | + workload_state -- valid juju workload state. |
273 | + message -- status update message |
274 | + """ |
275 | + valid_states = ['maintenance', 'blocked', 'waiting', 'active'] |
276 | + if workload_state not in valid_states: |
277 | + raise ValueError( |
278 | + '{!r} is not a valid workload state'.format(workload_state) |
279 | + ) |
280 | + cmd = ['status-set', workload_state, message] |
281 | + try: |
282 | + ret = subprocess.call(cmd) |
283 | + if ret == 0: |
284 | + return |
285 | + except OSError as e: |
286 | + if e.errno != errno.ENOENT: |
287 | + raise |
288 | + log_message = 'status-set failed: {} {}'.format(workload_state, |
289 | + message) |
290 | + log(log_message, level='INFO') |
291 | + |
292 | + |
293 | +def status_get(): |
294 | + """Retrieve the previously set juju workload state |
295 | + |
296 | + If the status-set command is not found then assume this is juju < 1.23 and |
297 | + return 'unknown' |
298 | + """ |
299 | + cmd = ['status-get'] |
300 | + try: |
301 | + raw_status = subprocess.check_output(cmd, universal_newlines=True) |
302 | + status = raw_status.rstrip() |
303 | + return status |
304 | + except OSError as e: |
305 | + if e.errno == errno.ENOENT: |
306 | + return 'unknown' |
307 | + else: |
308 | + raise |
309 | + |
310 | + |
311 | +def translate_exc(from_exc, to_exc): |
312 | + def inner_translate_exc1(f): |
313 | + def inner_translate_exc2(*args, **kwargs): |
314 | + try: |
315 | + return f(*args, **kwargs) |
316 | + except from_exc: |
317 | + raise to_exc |
318 | + |
319 | + return inner_translate_exc2 |
320 | + |
321 | + return inner_translate_exc1 |
322 | + |
323 | + |
324 | +@translate_exc(from_exc=OSError, to_exc=NotImplementedError) |
325 | +def is_leader(): |
326 | + """Does the current unit hold the juju leadership |
327 | + |
328 | + Uses juju to determine whether the current unit is the leader of its peers |
329 | + """ |
330 | + cmd = ['is-leader', '--format=json'] |
331 | + return json.loads(subprocess.check_output(cmd).decode('UTF-8')) |
332 | + |
333 | + |
334 | +@translate_exc(from_exc=OSError, to_exc=NotImplementedError) |
335 | +def leader_get(attribute=None): |
336 | + """Juju leader get value(s)""" |
337 | + cmd = ['leader-get', '--format=json'] + [attribute or '-'] |
338 | + return json.loads(subprocess.check_output(cmd).decode('UTF-8')) |
339 | + |
340 | + |
341 | +@translate_exc(from_exc=OSError, to_exc=NotImplementedError) |
342 | +def leader_set(settings=None, **kwargs): |
343 | + """Juju leader set value(s)""" |
344 | + log("Juju leader-set '%s'" % (settings), level=DEBUG) |
345 | + cmd = ['leader-set'] |
346 | + settings = settings or {} |
347 | + settings.update(kwargs) |
348 | + for k, v in settings.iteritems(): |
349 | + if v is None: |
350 | + cmd.append('{}='.format(k)) |
351 | + else: |
352 | + cmd.append('{}={}'.format(k, v)) |
353 | + subprocess.check_call(cmd) |
354 | |
355 | === modified file 'hooks/charmhelpers/core/host.py' |
356 | --- hooks/charmhelpers/core/host.py 2015-03-20 17:15:02 +0000 |
357 | +++ hooks/charmhelpers/core/host.py 2015-06-04 08:44:51 +0000 |
358 | @@ -90,7 +90,7 @@ |
359 | ['service', service_name, 'status'], |
360 | stderr=subprocess.STDOUT).decode('UTF-8') |
361 | except subprocess.CalledProcessError as e: |
362 | - return 'unrecognized service' not in e.output |
363 | + return b'unrecognized service' not in e.output |
364 | else: |
365 | return True |
366 | |
367 | |
368 | === modified file 'hooks/charmhelpers/core/services/base.py' |
369 | --- hooks/charmhelpers/core/services/base.py 2015-03-20 17:15:02 +0000 |
370 | +++ hooks/charmhelpers/core/services/base.py 2015-06-04 08:44:51 +0000 |
371 | @@ -15,9 +15,9 @@ |
372 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
373 | |
374 | import os |
375 | -import re |
376 | import json |
377 | -from collections import Iterable |
378 | +from inspect import getargspec |
379 | +from collections import Iterable, OrderedDict |
380 | |
381 | from charmhelpers.core import host |
382 | from charmhelpers.core import hookenv |
383 | @@ -119,7 +119,7 @@ |
384 | """ |
385 | self._ready_file = os.path.join(hookenv.charm_dir(), 'READY-SERVICES.json') |
386 | self._ready = None |
387 | - self.services = {} |
388 | + self.services = OrderedDict() |
389 | for service in services or []: |
390 | service_name = service['service'] |
391 | self.services[service_name] = service |
392 | @@ -132,8 +132,8 @@ |
393 | if hook_name == 'stop': |
394 | self.stop_services() |
395 | else: |
396 | + self.reconfigure_services() |
397 | self.provide_data() |
398 | - self.reconfigure_services() |
399 | cfg = hookenv.config() |
400 | if cfg.implicit_save: |
401 | cfg.save() |
402 | @@ -145,15 +145,36 @@ |
403 | A provider must have a `name` attribute, which indicates which relation |
404 | to set data on, and a `provide_data()` method, which returns a dict of |
405 | data to set. |
406 | + |
407 | + The `provide_data()` method can optionally accept two parameters: |
408 | + |
409 | + * ``remote_service`` The name of the remote service that the data will |
410 | + be provided to. The `provide_data()` method will be called once |
411 | + for each connected service (not unit). This allows the method to |
412 | + tailor its data to the given service. |
413 | + * ``service_ready`` Whether or not the service definition had all of |
414 | + its requirements met, and thus the ``data_ready`` callbacks run. |
415 | + |
416 | + Note that the ``provided_data`` methods are now called **after** the |
417 | + ``data_ready`` callbacks are run. This gives the ``data_ready`` callbacks |
418 | + a chance to generate any data necessary for the providing to the remote |
419 | + services. |
420 | """ |
421 | - hook_name = hookenv.hook_name() |
422 | - for service in self.services.values(): |
423 | + for service_name, service in self.services.items(): |
424 | + service_ready = self.is_ready(service_name) |
425 | for provider in service.get('provided_data', []): |
426 | - if re.match(r'{}-relation-(joined|changed)'.format(provider.name), hook_name): |
427 | - data = provider.provide_data() |
428 | - _ready = provider._is_ready(data) if hasattr(provider, '_is_ready') else data |
429 | - if _ready: |
430 | - hookenv.relation_set(None, data) |
431 | + for relid in hookenv.relation_ids(provider.name): |
432 | + units = hookenv.related_units(relid) |
433 | + if not units: |
434 | + continue |
435 | + remote_service = units[0].split('/')[0] |
436 | + argspec = getargspec(provider.provide_data) |
437 | + if len(argspec.args) > 1: |
438 | + data = provider.provide_data(remote_service, service_ready) |
439 | + else: |
440 | + data = provider.provide_data() |
441 | + if data: |
442 | + hookenv.relation_set(relid, data) |
443 | |
444 | def reconfigure_services(self, *service_names): |
445 | """ |
446 | |
447 | === modified file 'hooks/charmhelpers/fetch/__init__.py' |
448 | --- hooks/charmhelpers/fetch/__init__.py 2015-05-01 14:56:06 +0000 |
449 | +++ hooks/charmhelpers/fetch/__init__.py 2015-06-04 08:44:51 +0000 |
450 | @@ -158,7 +158,7 @@ |
451 | |
452 | def apt_cache(in_memory=True): |
453 | """Build and return an apt cache""" |
454 | - import apt_pkg |
455 | + from apt import apt_pkg |
456 | apt_pkg.init() |
457 | if in_memory: |
458 | apt_pkg.config.set("Dir::Cache::pkgcache", "") |
459 | |
460 | === modified file 'hooks/glance_relations.py' |
461 | --- hooks/glance_relations.py 2015-04-30 17:06:12 +0000 |
462 | +++ hooks/glance_relations.py 2015-06-04 08:44:51 +0000 |
463 | @@ -53,7 +53,7 @@ |
464 | filter_installed_packages |
465 | ) |
466 | from charmhelpers.contrib.hahelpers.cluster import ( |
467 | - eligible_leader, |
468 | + is_elected_leader, |
469 | get_hacluster_config |
470 | ) |
471 | from charmhelpers.contrib.openstack.utils import ( |
472 | @@ -160,7 +160,7 @@ |
473 | if rel != "essex": |
474 | CONFIGS.write(GLANCE_API_CONF) |
475 | |
476 | - if eligible_leader(CLUSTER_RES): |
477 | + if is_elected_leader(CLUSTER_RES): |
478 | # Bugs 1353135 & 1187508. Dbs can appear to be ready before the units |
479 | # acl entry has been added. So, if the db supports passing a list of |
480 | # permitted units then check if we're in the list. |
481 | @@ -194,7 +194,7 @@ |
482 | if rel != "essex": |
483 | CONFIGS.write(GLANCE_API_CONF) |
484 | |
485 | - if eligible_leader(CLUSTER_RES): |
486 | + if is_elected_leader(CLUSTER_RES): |
487 | if rel == "essex": |
488 | status = call(['glance-manage', 'db_version']) |
489 | if status != 0: |
490 | |
491 | === modified file 'hooks/glance_utils.py' |
492 | --- hooks/glance_utils.py 2015-05-12 19:46:43 +0000 |
493 | +++ hooks/glance_utils.py 2015-06-04 08:44:51 +0000 |
494 | @@ -42,7 +42,7 @@ |
495 | context,) |
496 | |
497 | from charmhelpers.contrib.hahelpers.cluster import ( |
498 | - eligible_leader, |
499 | + is_elected_leader, |
500 | ) |
501 | |
502 | from charmhelpers.contrib.openstack.alternatives import install_alternative |
503 | @@ -59,6 +59,10 @@ |
504 | |
505 | from charmhelpers.core.templating import render |
506 | |
507 | +from charmhelpers.core.decorators import ( |
508 | + retry_on_exception, |
509 | +) |
510 | + |
511 | CLUSTER_RES = "grp_glance_vips" |
512 | |
513 | PACKAGES = [ |
514 | @@ -219,6 +223,9 @@ |
515 | return configs |
516 | |
517 | |
518 | +# NOTE(jamespage): Retry deals with sync issues during one-shot HA deploys. |
519 | +# mysql might be restarting or suchlike. |
520 | +@retry_on_exception(5, base_delay=3, exc_type=subprocess.CalledProcessError) |
521 | def determine_packages(): |
522 | packages = [] + PACKAGES |
523 | |
524 | @@ -266,7 +273,7 @@ |
525 | configs.write_all() |
526 | |
527 | [service_stop(s) for s in services()] |
528 | - if eligible_leader(CLUSTER_RES): |
529 | + if is_elected_leader(CLUSTER_RES): |
530 | migrate_database() |
531 | [service_start(s) for s in services()] |
532 | |
533 | |
534 | === modified file 'unit_tests/test_glance_relations.py' |
535 | --- unit_tests/test_glance_relations.py 2015-05-12 19:46:43 +0000 |
536 | +++ unit_tests/test_glance_relations.py 2015-06-04 08:44:51 +0000 |
537 | @@ -41,12 +41,13 @@ |
538 | 'restart_on_change', |
539 | 'service_reload', |
540 | 'service_stop', |
541 | + 'service_restart', |
542 | # charmhelpers.contrib.openstack.utils |
543 | 'configure_installation_source', |
544 | 'os_release', |
545 | 'openstack_upgrade_available', |
546 | # charmhelpers.contrib.hahelpers.cluster_utils |
547 | - 'eligible_leader', |
548 | + 'is_elected_leader', |
549 | # glance_utils |
550 | 'restart_map', |
551 | 'register_configs', |
552 | @@ -431,6 +432,7 @@ |
553 | self.assertEquals([call('/etc/glance/glance-api.conf'), |
554 | call(self.ceph_config_file())], |
555 | configs.write.call_args_list) |
556 | + self.service_restart.assert_called_with('glance-api') |
557 | |
558 | @patch.object(relations, 'CONFIGS') |
559 | def test_ceph_broken(self, configs): |
560 | |
561 | === modified file 'unit_tests/test_glance_utils.py' |
562 | --- unit_tests/test_glance_utils.py 2015-05-08 12:35:25 +0000 |
563 | +++ unit_tests/test_glance_utils.py 2015-06-04 08:44:51 +0000 |
564 | @@ -16,7 +16,7 @@ |
565 | 'relation_ids', |
566 | 'get_os_codename_install_source', |
567 | 'configure_installation_source', |
568 | - 'eligible_leader', |
569 | + 'is_elected_leader', |
570 | 'templating', |
571 | 'apt_update', |
572 | 'apt_upgrade', |
573 | @@ -153,7 +153,7 @@ |
574 | git_requested.return_value = True |
575 | self.config.side_effect = None |
576 | self.config.return_value = 'cloud:precise-havana' |
577 | - self.eligible_leader.return_value = True |
578 | + self.is_elected_leader.return_value = True |
579 | self.get_os_codename_install_source.return_value = 'havana' |
580 | configs = MagicMock() |
581 | utils.do_openstack_upgrade(configs) |
582 | @@ -171,7 +171,7 @@ |
583 | git_requested.return_value = True |
584 | self.config.side_effect = None |
585 | self.config.return_value = 'cloud:precise-havana' |
586 | - self.eligible_leader.return_value = False |
587 | + self.is_elected_leader.return_value = False |
588 | self.get_os_codename_install_source.return_value = 'havana' |
589 | configs = MagicMock() |
590 | utils.do_openstack_upgrade(configs) |