Merge lp:~thedac/charms/trusty/neutron-api/status into lp:~openstack-charmers-archive/charms/trusty/neutron-api/next
- Trusty Tahr (14.04)
- status
- Merge into next
Status: | Work in progress |
---|---|
Proposed branch: | lp:~thedac/charms/trusty/neutron-api/status |
Merge into: | lp:~openstack-charmers-archive/charms/trusty/neutron-api/next |
Diff against target: |
638 lines (+246/-37) 6 files modified
hooks/charmhelpers/contrib/openstack/context.py (+8/-0) hooks/charmhelpers/contrib/openstack/utils.py (+83/-1) hooks/charmhelpers/core/hookenv.py (+84/-16) hooks/neutron_api_hooks.py (+25/-1) hooks/neutron_api_utils.py (+8/-0) unit_tests/test_neutron_api_hooks.py (+38/-19) |
To merge this branch: | bzr merge lp:~thedac/charms/trusty/neutron-api/status |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Page | Needs Fixing | ||
Review via email: mp+264315@code.launchpad.net |
Commit message
Add workload status settings
Description of the change
Based on Liam Young's original work add workload status settings to the neutron-api charm as a proof of concept.
The plan is to move context_status, set_context_status and incomplete_contexts to charmhelpers.
Looking for feedback before proceeding.
- 118. By David Ames
-
More status sets for install process
- 119. By David Ames
-
Use functools wraps
- 120. By David Ames
-
With @wraps in the decorator can now apply context_status to *_joined functions
- 121. By David Ames
-
Set status when waiting on a particular variable in a given context
Compound the status messages keeping the highest priority workload state
David Ames (thedac) wrote : | # |
charmhelpers.
It is important to note that in practice these statuses are ephemeral as the context_status decorator comes around and runs set_context_status which is a bit more of a hammer. This is necessary to make sure we set a known state rather than just the last set state wins.
I am very much open to ideas to get us further granularity into set_context_status. set_context_status relies on charmhelpers.
ates.OSConfigRe
- 122. By David Ames
-
Updates to status_set
- 123. By David Ames
-
Set workload state waiting or blocked
- 124. By David Ames
-
Let the decorator set the status
- 125. By David Ames
-
Fix unit tests for new related(interface) check
Unmerged revisions
- 125. By David Ames
-
Fix unit tests for new related(interface) check
- 124. By David Ames
-
Let the decorator set the status
- 123. By David Ames
-
Set workload state waiting or blocked
Preview Diff
1 | === modified file 'hooks/charmhelpers/contrib/openstack/context.py' |
2 | --- hooks/charmhelpers/contrib/openstack/context.py 2015-07-16 20:17:53 +0000 |
3 | +++ hooks/charmhelpers/contrib/openstack/context.py 2015-07-17 22:28:29 +0000 |
4 | @@ -37,6 +37,8 @@ |
5 | relation_ids, |
6 | related_units, |
7 | relation_set, |
8 | + status_compound, |
9 | + status_get, |
10 | unit_get, |
11 | unit_private_ip, |
12 | charm_name, |
13 | @@ -101,11 +103,17 @@ |
14 | |
15 | def context_complete(ctxt): |
16 | _missing = [] |
17 | + # keys are set by default even before the relation completes them |
18 | + # i.e. rabbitmq-password: None |
19 | + # so if they are None or '' they are missing from the relation |
20 | for k, v in six.iteritems(ctxt): |
21 | if v is None or v == '': |
22 | _missing.append(k) |
23 | |
24 | if _missing: |
25 | + status_compound(status_get(include_data=True), 'waiting', |
26 | + 'Missing required data: %s' % |
27 | + ' '.join(_missing)) |
28 | log('Missing required data: %s' % ' '.join(_missing), level=INFO) |
29 | return False |
30 | |
31 | |
32 | === modified file 'hooks/charmhelpers/contrib/openstack/utils.py' |
33 | --- hooks/charmhelpers/contrib/openstack/utils.py 2015-07-16 20:17:53 +0000 |
34 | +++ hooks/charmhelpers/contrib/openstack/utils.py 2015-07-17 22:28:29 +0000 |
35 | @@ -40,7 +40,8 @@ |
36 | charm_dir, |
37 | INFO, |
38 | relation_ids, |
39 | - relation_set |
40 | + relation_set, |
41 | + status_set |
42 | ) |
43 | |
44 | from charmhelpers.contrib.storage.linux.lvm import ( |
45 | @@ -704,3 +705,84 @@ |
46 | return projects[key] |
47 | |
48 | return None |
49 | + |
50 | + |
51 | +def context_status(configs, required_interfaces): |
52 | + """ |
53 | + Decorator to set workload status based on complete contexts |
54 | + after contexts have been acted on |
55 | + """ |
56 | + def wrap(f): |
57 | + @wraps(f) |
58 | + def wrapped_f(*args, **kwargs): |
59 | + # Run the original function first |
60 | + f(*args, **kwargs) |
61 | + # Set workload status now that contexts have been |
62 | + # acted on |
63 | + set_context_status(configs, required_interfaces) |
64 | + return wrapped_f |
65 | + return wrap |
66 | + |
67 | + |
68 | +def set_context_status(configs, required_interfaces): |
69 | + """ |
70 | + Set workload status based on complete contexts |
71 | + Left outside of the decorator, context_status, |
72 | + to allow manual use |
73 | + """ |
74 | + incomplete_ctxts = incomplete_contexts(configs, required_interfaces) |
75 | + state = 'active' |
76 | + message = '' |
77 | + for context in incomplete_ctxts: |
78 | + related_interface = None |
79 | + for interface in required_interfaces[context]: |
80 | + if related(interface): |
81 | + related_interface = interface |
82 | + if not related_interface: |
83 | + message += "{} context is missing and must be related for " \ |
84 | + "functionality. ".format(context) |
85 | + state = 'blocked' |
86 | + else: |
87 | + message += "{} context's interface, {}, has joined but has not " \ |
88 | + "yet provided the required data on the relation. " \ |
89 | + "".format(context, related_interface) |
90 | + if state != 'blocked': |
91 | + state = 'waiting' |
92 | + |
93 | + if state == 'active': |
94 | + message = "All required contexts are present and complete" |
95 | + |
96 | + status_set(state, message) |
97 | + |
98 | + |
99 | +def incomplete_contexts(configs, required_interfaces): |
100 | + """ |
101 | + Check complete contexts against required_interfaces |
102 | + Return list of incomplete contexts |
103 | + |
104 | + configs is an OSConfigRenderer object with configs registered |
105 | + |
106 | + required_interfaces is a dictionary of required general interfaces |
107 | + with list values of possible specific interfaces. |
108 | + The interface is said to be satisfied if anyone of the interfaces in the |
109 | + list has a complete context. |
110 | + Example: |
111 | + required_interfaces = {'database': ['shared-db', 'pgsql-db']} |
112 | + """ |
113 | + complete_ctxts = configs.complete_contexts() |
114 | + incomplete_contexts = [] |
115 | + for svc_type in required_interfaces.keys(): |
116 | + found_ctxt = False |
117 | + for interface in required_interfaces[svc_type]: |
118 | + if interface in complete_ctxts: |
119 | + found_ctxt = True |
120 | + if not found_ctxt: |
121 | + incomplete_contexts.append(svc_type) |
122 | + return incomplete_contexts |
123 | + |
124 | + |
125 | +def related(interface): |
126 | + if relation_ids(interface): |
127 | + return True |
128 | + else: |
129 | + return False |
130 | |
131 | === modified file 'hooks/charmhelpers/core/hookenv.py' |
132 | --- hooks/charmhelpers/core/hookenv.py 2015-07-16 20:17:53 +0000 |
133 | +++ hooks/charmhelpers/core/hookenv.py 2015-07-17 22:28:29 +0000 |
134 | @@ -660,35 +660,103 @@ |
135 | ) |
136 | cmd = ['status-set', workload_state, message] |
137 | try: |
138 | - ret = subprocess.call(cmd) |
139 | - if ret == 0: |
140 | + subprocess.call(cmd) |
141 | + except OSError as e: |
142 | + if e.errno == errno.ENOENT: |
143 | + log_message = 'status-set failed: {} {}'.format(workload_state, |
144 | + message) |
145 | + log(log_message, level='INFO') |
146 | return |
147 | - except OSError as e: |
148 | - if e.errno != errno.ENOENT: |
149 | + else: |
150 | raise |
151 | - log_message = 'status-set failed: {} {}'.format(workload_state, |
152 | - message) |
153 | - log(log_message, level='INFO') |
154 | - |
155 | - |
156 | -def status_get(): |
157 | - """Retrieve the previously set juju workload state |
158 | + # Log the status change |
159 | + if workload_state == 'blocked' or workload_state == 'waiting': |
160 | + lvl = 'WARN' |
161 | + else: |
162 | + lvl = 'INFO' |
163 | + log('{}: {}'.format(workload_state, message), lvl) |
164 | + |
165 | + |
166 | +def status_get(include_data=True, service=False): |
167 | + """Retrieve the previously set juju workload state and return as a |
168 | + dictionary. |
169 | + |
170 | + If service is set return status for all units of this service if this unit |
171 | + is the leader |
172 | |
173 | If the status-set command is not found then assume this is juju < 1.23 and |
174 | return 'unknown' |
175 | """ |
176 | - cmd = ['status-get'] |
177 | + cmd = ['status-get', '--format', 'json'] |
178 | + if include_data: |
179 | + cmd.append('--include-data') |
180 | + if service: |
181 | + cmd.append('--service') |
182 | try: |
183 | - raw_status = subprocess.check_output(cmd, universal_newlines=True) |
184 | - status = raw_status.rstrip() |
185 | - return status |
186 | + return json.loads(subprocess.check_output(cmd, |
187 | + universal_newlines=True)) |
188 | except OSError as e: |
189 | if e.errno == errno.ENOENT: |
190 | - return 'unknown' |
191 | + return {'status': 'unknown'} |
192 | else: |
193 | raise |
194 | |
195 | |
196 | +def status_compound(current_status, workload_state, message): |
197 | + """Compound statuses: Take the current status as a dictionary |
198 | + and new workload state and message then status_set with the highest |
199 | + severity workload state and a compound message |
200 | + Example: |
201 | + status_compound(status_get(include-data=True), |
202 | + 'waiting', |
203 | + 'waiting on relations data') |
204 | + """ |
205 | + hierarchy = {'unknown': -1, |
206 | + 'active': 0, |
207 | + 'maintenance': 1, |
208 | + 'waiting': 2, |
209 | + 'blocked': 3, |
210 | + } |
211 | + current_workload_state = current_status.get('status') |
212 | + current_message = current_status.get('message') |
213 | + |
214 | + # Do not set unknown or other invalid state |
215 | + if hierarchy.get(workload_state) is None: |
216 | + return {} |
217 | + elif hierarchy.get(workload_state) < 0: |
218 | + return {} |
219 | + if hierarchy.get(current_workload_state) is None: |
220 | + current_workload_state = 'unknown' |
221 | + current_message = None |
222 | + elif hierarchy.get(current_workload_state) < 0: |
223 | + current_message = None |
224 | + |
225 | + # New state of 'active' overrides all others |
226 | + if workload_state == 'active': |
227 | + hierarchy['active'] = 10 |
228 | + current_message = None |
229 | + |
230 | + # Set workload_state based on hierarchy of statuses |
231 | + if hierarchy.get(current_workload_state) > hierarchy.get(workload_state): |
232 | + workload_state = current_workload_state |
233 | + else: |
234 | + if current_workload_state == 'active': |
235 | + current_message = None |
236 | + |
237 | + # Compound the message |
238 | + if current_message and message: |
239 | + message = message + "; " + current_message |
240 | + elif current_message and not message: |
241 | + message = current_message |
242 | + |
243 | + # Set new status |
244 | + if workload_state: |
245 | + status_set(workload_state, message) |
246 | + |
247 | + # Return new status |
248 | + return {'status': workload_state, 'message': message} |
249 | + |
250 | + |
251 | def translate_exc(from_exc, to_exc): |
252 | def inner_translate_exc1(f): |
253 | def inner_translate_exc2(*args, **kwargs): |
254 | |
255 | === modified file 'hooks/neutron_api_hooks.py' |
256 | --- hooks/neutron_api_hooks.py 2015-04-23 08:49:03 +0000 |
257 | +++ hooks/neutron_api_hooks.py 2015-07-17 22:28:29 +0000 |
258 | @@ -17,6 +17,7 @@ |
259 | relation_get, |
260 | relation_ids, |
261 | relation_set, |
262 | + status_set, |
263 | open_port, |
264 | unit_get, |
265 | ) |
266 | @@ -36,6 +37,7 @@ |
267 | from charmhelpers.contrib.openstack.utils import ( |
268 | config_value_changed, |
269 | configure_installation_source, |
270 | + context_status, |
271 | git_install_requested, |
272 | openstack_upgrade_available, |
273 | os_requires_version, |
274 | @@ -46,6 +48,7 @@ |
275 | from neutron_api_utils import ( |
276 | CLUSTER_RES, |
277 | NEUTRON_CONF, |
278 | + REQUIRED_INTERFACES, |
279 | api_port, |
280 | determine_packages, |
281 | determine_ports, |
282 | @@ -140,14 +143,18 @@ |
283 | |
284 | |
285 | @hooks.hook() |
286 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
287 | def install(): |
288 | + status_set('maintenance', 'Executing pre-install') |
289 | execd_preinstall() |
290 | configure_installation_source(config('openstack-origin')) |
291 | |
292 | + status_set('maintenance', 'Installing apt packages') |
293 | apt_update() |
294 | apt_install(determine_packages(config('openstack-origin')), |
295 | fatal=True) |
296 | |
297 | + status_set('maintenance', 'Git install') |
298 | git_install(config('openstack-origin-git')) |
299 | |
300 | [open_port(port) for port in determine_ports()] |
301 | @@ -155,6 +162,7 @@ |
302 | |
303 | @hooks.hook('upgrade-charm') |
304 | @hooks.hook('config-changed') |
305 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
306 | @restart_on_change(restart_map(), stopstart=True) |
307 | def config_changed(): |
308 | # If neutron is ready to be queried then check for incompatability between |
309 | @@ -163,14 +171,16 @@ |
310 | if l3ha_router_present() and not get_l3ha(): |
311 | e = ('Cannot disable Router HA while ha enabled routers exist.' |
312 | ' Please remove any ha routers') |
313 | - log(e, level=ERROR) |
314 | + status_set('blocked', e) |
315 | raise Exception(e) |
316 | if dvr_router_present() and not get_dvr(): |
317 | e = ('Cannot disable dvr while dvr enabled routers exist. Please' |
318 | ' remove any distributed routers') |
319 | log(e, level=ERROR) |
320 | + status_set('blocked', e) |
321 | raise Exception(e) |
322 | if config('prefer-ipv6'): |
323 | + status_set('maintenance', 'configuring ipv6') |
324 | setup_ipv6() |
325 | sync_db_with_multi_ipv6_addresses(config('database'), |
326 | config('database-user')) |
327 | @@ -178,11 +188,14 @@ |
328 | global CONFIGS |
329 | if git_install_requested(): |
330 | if config_value_changed('openstack-origin-git'): |
331 | + status_set('maintenance', 'Running Git install') |
332 | git_install(config('openstack-origin-git')) |
333 | else: |
334 | if openstack_upgrade_available('neutron-server'): |
335 | + status_set('maintenance', 'Running openstack upgrade') |
336 | do_openstack_upgrade(CONFIGS) |
337 | |
338 | + status_set('maintenance', 'Installing apt packages') |
339 | apt_install(filter_installed_packages( |
340 | determine_packages(config('openstack-origin'))), |
341 | fatal=True) |
342 | @@ -203,6 +216,7 @@ |
343 | |
344 | |
345 | @hooks.hook('amqp-relation-joined') |
346 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
347 | def amqp_joined(relation_id=None): |
348 | relation_set(relation_id=relation_id, |
349 | username=config('rabbit-user'), vhost=config('rabbit-vhost')) |
350 | @@ -210,6 +224,7 @@ |
351 | |
352 | @hooks.hook('amqp-relation-changed') |
353 | @hooks.hook('amqp-relation-departed') |
354 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
355 | @restart_on_change(restart_map()) |
356 | def amqp_changed(): |
357 | if 'amqp' not in CONFIGS.complete_contexts(): |
358 | @@ -219,6 +234,7 @@ |
359 | |
360 | |
361 | @hooks.hook('shared-db-relation-joined') |
362 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
363 | def db_joined(): |
364 | if is_relation_made('pgsql-db'): |
365 | # error, postgresql is used |
366 | @@ -238,6 +254,7 @@ |
367 | |
368 | |
369 | @hooks.hook('pgsql-db-relation-joined') |
370 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
371 | def pgsql_neutron_db_joined(): |
372 | if is_relation_made('shared-db'): |
373 | # raise error |
374 | @@ -250,6 +267,7 @@ |
375 | |
376 | |
377 | @hooks.hook('shared-db-relation-changed') |
378 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
379 | @restart_on_change(restart_map()) |
380 | def db_changed(): |
381 | if 'shared-db' not in CONFIGS.complete_contexts(): |
382 | @@ -260,6 +278,7 @@ |
383 | |
384 | |
385 | @hooks.hook('pgsql-db-relation-changed') |
386 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
387 | @restart_on_change(restart_map()) |
388 | def postgresql_neutron_db_changed(): |
389 | CONFIGS.write(NEUTRON_CONF) |
390 | @@ -270,11 +289,13 @@ |
391 | 'identity-service-relation-broken', |
392 | 'shared-db-relation-broken', |
393 | 'pgsql-db-relation-broken') |
394 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
395 | def relation_broken(): |
396 | CONFIGS.write_all() |
397 | |
398 | |
399 | @hooks.hook('identity-service-relation-joined') |
400 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
401 | def identity_joined(rid=None, relation_trigger=False): |
402 | public_url = '{}:{}'.format(canonical_url(CONFIGS, PUBLIC), |
403 | api_port('neutron-server')) |
404 | @@ -296,6 +317,7 @@ |
405 | |
406 | |
407 | @hooks.hook('identity-service-relation-changed') |
408 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
409 | @restart_on_change(restart_map()) |
410 | def identity_changed(): |
411 | if 'identity-service' not in CONFIGS.complete_contexts(): |
412 | @@ -472,6 +494,7 @@ |
413 | |
414 | |
415 | @hooks.hook('zeromq-configuration-relation-joined') |
416 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
417 | @os_requires_version('kilo', 'neutron-server') |
418 | def zeromq_configuration_relation_joined(relid=None): |
419 | relation_set(relation_id=relid, |
420 | @@ -480,6 +503,7 @@ |
421 | |
422 | |
423 | @hooks.hook('zeromq-configuration-relation-changed') |
424 | +@context_status(CONFIGS, REQUIRED_INTERFACES) |
425 | @restart_on_change(restart_map(), stopstart=True) |
426 | def zeromq_configuration_relation_changed(): |
427 | CONFIGS.write_all() |
428 | |
429 | === modified file 'hooks/neutron_api_utils.py' |
430 | --- hooks/neutron_api_utils.py 2015-07-13 19:07:37 +0000 |
431 | +++ hooks/neutron_api_utils.py 2015-07-17 22:28:29 +0000 |
432 | @@ -155,6 +155,14 @@ |
433 | }), |
434 | ]) |
435 | |
436 | +# The interface is said to be satisfied if anyone of the interfaces in the |
437 | +# list has a complete context. |
438 | +REQUIRED_INTERFACES = { |
439 | + 'database': ['shared-db', 'pgsql-db'], |
440 | + 'message': ['amqp', 'zeromq-configuration'], |
441 | + 'identity': ['identity-service'], |
442 | +} |
443 | + |
444 | |
445 | def api_port(service): |
446 | return API_PORTS[service] |
447 | |
448 | === modified file 'unit_tests/test_neutron_api_hooks.py' |
449 | --- unit_tests/test_neutron_api_hooks.py 2015-06-04 23:28:14 +0000 |
450 | +++ unit_tests/test_neutron_api_hooks.py 2015-07-17 22:28:29 +0000 |
451 | @@ -96,8 +96,9 @@ |
452 | hooks.hooks.execute([ |
453 | 'hooks/{}'.format(hookname)]) |
454 | |
455 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
456 | @patch.object(utils, 'git_install_requested') |
457 | - def test_install_hook(self, git_requested): |
458 | + def test_install_hook(self, git_requested, related): |
459 | git_requested.return_value = False |
460 | _pkgs = ['foo', 'bar'] |
461 | _ports = [80, 81, 82] |
462 | @@ -115,8 +116,9 @@ |
463 | self.open_port.assert_has_calls(_port_calls) |
464 | self.assertTrue(self.execd_preinstall.called) |
465 | |
466 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
467 | @patch.object(utils, 'git_install_requested') |
468 | - def test_install_hook_git(self, git_requested): |
469 | + def test_install_hook_git(self, git_requested, related): |
470 | git_requested.return_value = True |
471 | _pkgs = ['foo', 'bar'] |
472 | _ports = [80, 81, 82] |
473 | @@ -148,9 +150,10 @@ |
474 | self.git_install.assert_called_with(projects_yaml) |
475 | self.open_port.assert_has_calls(_port_calls) |
476 | |
477 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
478 | @patch.object(hooks, 'configure_https') |
479 | @patch.object(hooks, 'git_install_requested') |
480 | - def test_config_changed(self, git_requested, conf_https): |
481 | + def test_config_changed(self, git_requested, conf_https, related): |
482 | git_requested.return_value = False |
483 | self.neutron_ready.return_value = True |
484 | self.openstack_upgrade_available.return_value = True |
485 | @@ -196,11 +199,12 @@ |
486 | 'Cannot disable Router HA while ha enabled routers' |
487 | ' exist. Please remove any ha routers') |
488 | |
489 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
490 | @patch.object(hooks, 'configure_https') |
491 | @patch.object(hooks, 'git_install_requested') |
492 | @patch.object(hooks, 'config_value_changed') |
493 | def test_config_changed_git(self, config_val_changed, git_requested, |
494 | - configure_https): |
495 | + configure_https, related): |
496 | git_requested.return_value = True |
497 | self.neutron_ready.return_value = True |
498 | self.dvr_router_present.return_value = False |
499 | @@ -243,7 +247,8 @@ |
500 | self.assertTrue(_zmq_joined.called) |
501 | self.assertTrue(_id_cluster_joined.called) |
502 | |
503 | - def test_amqp_joined(self): |
504 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
505 | + def test_amqp_joined(self, related): |
506 | self._call_hook('amqp-relation-joined') |
507 | self.relation_set.assert_called_with( |
508 | username='neutron', |
509 | @@ -251,16 +256,19 @@ |
510 | relation_id=None |
511 | ) |
512 | |
513 | - def test_amqp_changed(self): |
514 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
515 | + def test_amqp_changed(self, related): |
516 | self.CONFIGS.complete_contexts.return_value = ['amqp'] |
517 | self._call_hook('amqp-relation-changed') |
518 | self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF)) |
519 | |
520 | - def test_amqp_departed(self): |
521 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
522 | + def test_amqp_departed(self, related): |
523 | self._call_hook('amqp-relation-departed') |
524 | self.assertTrue(self.CONFIGS.write.called_with(NEUTRON_CONF)) |
525 | |
526 | - def test_db_joined(self): |
527 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
528 | + def test_db_joined(self, related): |
529 | self.is_relation_made.return_value = False |
530 | self.unit_get.return_value = 'myhostname' |
531 | self._call_hook('shared-db-relation-joined') |
532 | @@ -270,7 +278,8 @@ |
533 | hostname='myhostname', |
534 | ) |
535 | |
536 | - def test_db_joined_with_postgresql(self): |
537 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
538 | + def test_db_joined_with_postgresql(self, related): |
539 | self.is_relation_made.return_value = True |
540 | |
541 | with self.assertRaises(Exception) as context: |
542 | @@ -279,7 +288,8 @@ |
543 | 'Attempting to associate a mysql database when there ' |
544 | 'is already associated a postgresql one') |
545 | |
546 | - def test_postgresql_db_joined(self): |
547 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
548 | + def test_postgresql_db_joined(self, related): |
549 | self.unit_get.return_value = 'myhostname' |
550 | self.is_relation_made.return_value = False |
551 | self._call_hook('pgsql-db-relation-joined') |
552 | @@ -287,7 +297,8 @@ |
553 | database='neutron', |
554 | ) |
555 | |
556 | - def test_postgresql_joined_with_db(self): |
557 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
558 | + def test_postgresql_joined_with_db(self, related): |
559 | self.is_relation_made.return_value = True |
560 | |
561 | with self.assertRaises(Exception) as context: |
562 | @@ -296,30 +307,35 @@ |
563 | 'Attempting to associate a postgresql database when' |
564 | ' there is already associated a mysql one') |
565 | |
566 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
567 | @patch.object(hooks, 'conditional_neutron_migration') |
568 | - def test_shared_db_changed(self, cond_neutron_mig): |
569 | + def test_shared_db_changed(self, cond_neutron_mig, related): |
570 | self.CONFIGS.complete_contexts.return_value = ['shared-db'] |
571 | self._call_hook('shared-db-relation-changed') |
572 | self.assertTrue(self.CONFIGS.write_all.called) |
573 | cond_neutron_mig.assert_called_with() |
574 | |
575 | - def test_shared_db_changed_partial_ctxt(self): |
576 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
577 | + def test_shared_db_changed_partial_ctxt(self, related): |
578 | self.CONFIGS.complete_contexts.return_value = [] |
579 | self._call_hook('shared-db-relation-changed') |
580 | self.assertFalse(self.CONFIGS.write_all.called) |
581 | |
582 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
583 | @patch.object(hooks, 'conditional_neutron_migration') |
584 | - def test_pgsql_db_changed(self, cond_neutron_mig): |
585 | + def test_pgsql_db_changed(self, cond_neutron_mig, related): |
586 | self._call_hook('pgsql-db-relation-changed') |
587 | self.assertTrue(self.CONFIGS.write.called) |
588 | cond_neutron_mig.assert_called_with() |
589 | |
590 | - def test_amqp_broken(self): |
591 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=False) |
592 | + def test_amqp_broken(self, related): |
593 | self._call_hook('amqp-relation-broken') |
594 | self.assertTrue(self.CONFIGS.write_all.called) |
595 | |
596 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
597 | @patch.object(hooks, 'canonical_url') |
598 | - def test_identity_joined(self, _canonical_url): |
599 | + def test_identity_joined(self, _canonical_url, related): |
600 | _canonical_url.return_value = 'http://127.0.0.1' |
601 | self.api_port.return_value = '9696' |
602 | self.test_config.set('region', 'region1') |
603 | @@ -337,13 +353,14 @@ |
604 | relation_settings=_endpoints |
605 | ) |
606 | |
607 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
608 | @patch('charmhelpers.contrib.openstack.ip.service_name', |
609 | lambda *args: 'neutron-api') |
610 | @patch('charmhelpers.contrib.openstack.ip.unit_get') |
611 | @patch('charmhelpers.contrib.openstack.ip.is_clustered') |
612 | @patch('charmhelpers.contrib.openstack.ip.config') |
613 | def test_identity_changed_public_name(self, _config, _is_clustered, |
614 | - _unit_get): |
615 | + _unit_get, related): |
616 | _unit_get.return_value = '127.0.0.1' |
617 | _is_clustered.return_value = False |
618 | _config.side_effect = self.test_config.get |
619 | @@ -365,15 +382,17 @@ |
620 | relation_settings=_endpoints |
621 | ) |
622 | |
623 | - def test_identity_changed_partial_ctxt(self): |
624 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
625 | + def test_identity_changed_partial_ctxt(self, related): |
626 | self.CONFIGS.complete_contexts.return_value = [] |
627 | _api_rel_joined = self.patch('neutron_api_relation_joined') |
628 | self.relation_ids.side_effect = self._fake_relids |
629 | self._call_hook('identity-service-relation-changed') |
630 | self.assertFalse(_api_rel_joined.called) |
631 | |
632 | + @patch('charmhelpers.contrib.openstack.utils.related', return_value=True) |
633 | @patch.object(hooks, 'configure_https') |
634 | - def test_identity_changed(self, conf_https): |
635 | + def test_identity_changed(self, conf_https, related): |
636 | self.CONFIGS.complete_contexts.return_value = ['identity-service'] |
637 | _api_rel_joined = self.patch('neutron_api_relation_joined') |
638 | self.relation_ids.side_effect = self._fake_relids |
Status functions moved to charm helpers
Please also see /code.launchpad .net/~thedac/ charm-helpers/ openstack- workload- status/ +merge/ 264353
https:/