Merge lp:~james-page/charms/trusty/nova-cloud-controller/lp1531102-trunk into lp:~openstack-charmers-archive/charms/trusty/nova-cloud-controller/trunk

Proposed by James Page
Status: Merged
Merged at revision: 172
Proposed branch: lp:~james-page/charms/trusty/nova-cloud-controller/lp1531102-trunk
Merge into: lp:~openstack-charmers-archive/charms/trusty/nova-cloud-controller/trunk
Diff against target: 670 lines (+292/-42)
9 files modified
hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+100/-1)
hooks/charmhelpers/contrib/openstack/amulet/utils.py (+25/-3)
hooks/charmhelpers/contrib/openstack/context.py (+25/-9)
hooks/charmhelpers/contrib/openstack/neutron.py (+14/-0)
hooks/charmhelpers/contrib/openstack/utils.py (+22/-17)
hooks/charmhelpers/core/host.py (+12/-1)
hooks/charmhelpers/core/hugepage.py (+2/-0)
tests/charmhelpers/contrib/openstack/amulet/deployment.py (+67/-8)
tests/charmhelpers/contrib/openstack/amulet/utils.py (+25/-3)
To merge this branch: bzr merge lp:~james-page/charms/trusty/nova-cloud-controller/lp1531102-trunk
Reviewer Review Type Date Requested Status
OpenStack Charmers Pending
Review via email: mp+281867@code.launchpad.net

Commit message

Resync helpers

Description of the change

Resync stable helpers to fixup issues with liberty point releases.

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

charm_lint_check #16735 nova-cloud-controller for james-page mp281867
    LINT OK: passed

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

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

charm_unit_test #15639 nova-cloud-controller for james-page mp281867
    UNIT OK: passed

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

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

charm_amulet_test #8558 nova-cloud-controller for james-page mp281867
    AMULET FAIL: amulet-test failed

AMULET Results (max last 2 lines):
Timeout occurred (2700s), printing juju status...environment: osci-sv07
ERROR:root:Make target returned non-zero.

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

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

charm_amulet_test #8709 nova-cloud-controller for james-page mp281867
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/8709/

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

charm_amulet_test #8710 nova-cloud-controller for james-page mp281867
    AMULET OK: passed

Build: http://10.245.162.77:8080/job/charm_amulet_test/8710/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py'
2--- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2015-10-22 13:22:39 +0000
3+++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2016-01-07 14:24:40 +0000
4@@ -14,12 +14,18 @@
5 # You should have received a copy of the GNU Lesser General Public License
6 # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
7
8+import logging
9+import re
10+import sys
11 import six
12 from collections import OrderedDict
13 from charmhelpers.contrib.amulet.deployment import (
14 AmuletDeployment
15 )
16
17+DEBUG = logging.DEBUG
18+ERROR = logging.ERROR
19+
20
21 class OpenStackAmuletDeployment(AmuletDeployment):
22 """OpenStack amulet deployment.
23@@ -28,9 +34,12 @@
24 that is specifically for use by OpenStack charms.
25 """
26
27- def __init__(self, series=None, openstack=None, source=None, stable=True):
28+ def __init__(self, series=None, openstack=None, source=None,
29+ stable=True, log_level=DEBUG):
30 """Initialize the deployment environment."""
31 super(OpenStackAmuletDeployment, self).__init__(series)
32+ self.log = self.get_logger(level=log_level)
33+ self.log.info('OpenStackAmuletDeployment: init')
34 self.openstack = openstack
35 self.source = source
36 self.stable = stable
37@@ -38,6 +47,22 @@
38 # out.
39 self.current_next = "trusty"
40
41+ def get_logger(self, name="deployment-logger", level=logging.DEBUG):
42+ """Get a logger object that will log to stdout."""
43+ log = logging
44+ logger = log.getLogger(name)
45+ fmt = log.Formatter("%(asctime)s %(funcName)s "
46+ "%(levelname)s: %(message)s")
47+
48+ handler = log.StreamHandler(stream=sys.stdout)
49+ handler.setLevel(level)
50+ handler.setFormatter(fmt)
51+
52+ logger.addHandler(handler)
53+ logger.setLevel(level)
54+
55+ return logger
56+
57 def _determine_branch_locations(self, other_services):
58 """Determine the branch locations for the other services.
59
60@@ -45,6 +70,8 @@
61 stable or next (dev) branch, and based on this, use the corresonding
62 stable or next branches for the other_services."""
63
64+ self.log.info('OpenStackAmuletDeployment: determine branch locations')
65+
66 # Charms outside the lp:~openstack-charmers namespace
67 base_charms = ['mysql', 'mongodb', 'nrpe']
68
69@@ -82,6 +109,8 @@
70
71 def _add_services(self, this_service, other_services):
72 """Add services to the deployment and set openstack-origin/source."""
73+ self.log.info('OpenStackAmuletDeployment: adding services')
74+
75 other_services = self._determine_branch_locations(other_services)
76
77 super(OpenStackAmuletDeployment, self)._add_services(this_service,
78@@ -111,9 +140,79 @@
79
80 def _configure_services(self, configs):
81 """Configure all of the services."""
82+ self.log.info('OpenStackAmuletDeployment: configure services')
83 for service, config in six.iteritems(configs):
84 self.d.configure(service, config)
85
86+ def _auto_wait_for_status(self, message=None, exclude_services=None,
87+ include_only=None, timeout=1800):
88+ """Wait for all units to have a specific extended status, except
89+ for any defined as excluded. Unless specified via message, any
90+ status containing any case of 'ready' will be considered a match.
91+
92+ Examples of message usage:
93+
94+ Wait for all unit status to CONTAIN any case of 'ready' or 'ok':
95+ message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE)
96+
97+ Wait for all units to reach this status (exact match):
98+ message = re.compile('^Unit is ready and clustered$')
99+
100+ Wait for all units to reach any one of these (exact match):
101+ message = re.compile('Unit is ready|OK|Ready')
102+
103+ Wait for at least one unit to reach this status (exact match):
104+ message = {'ready'}
105+
106+ See Amulet's sentry.wait_for_messages() for message usage detail.
107+ https://github.com/juju/amulet/blob/master/amulet/sentry.py
108+
109+ :param message: Expected status match
110+ :param exclude_services: List of juju service names to ignore,
111+ not to be used in conjuction with include_only.
112+ :param include_only: List of juju service names to exclusively check,
113+ not to be used in conjuction with exclude_services.
114+ :param timeout: Maximum time in seconds to wait for status match
115+ :returns: None. Raises if timeout is hit.
116+ """
117+ self.log.info('Waiting for extended status on units...')
118+
119+ all_services = self.d.services.keys()
120+
121+ if exclude_services and include_only:
122+ raise ValueError('exclude_services can not be used '
123+ 'with include_only')
124+
125+ if message:
126+ if isinstance(message, re._pattern_type):
127+ match = message.pattern
128+ else:
129+ match = message
130+
131+ self.log.debug('Custom extended status wait match: '
132+ '{}'.format(match))
133+ else:
134+ self.log.debug('Default extended status wait match: contains '
135+ 'READY (case-insensitive)')
136+ message = re.compile('.*ready.*', re.IGNORECASE)
137+
138+ if exclude_services:
139+ self.log.debug('Excluding services from extended status match: '
140+ '{}'.format(exclude_services))
141+ else:
142+ exclude_services = []
143+
144+ if include_only:
145+ services = include_only
146+ else:
147+ services = list(set(all_services) - set(exclude_services))
148+
149+ self.log.debug('Waiting up to {}s for extended status on services: '
150+ '{}'.format(timeout, services))
151+ service_messages = {service: message for service in services}
152+ self.d.sentry.wait_for_messages(service_messages, timeout=timeout)
153+ self.log.info('OK')
154+
155 def _get_openstack_release(self):
156 """Get openstack release.
157
158
159=== modified file 'hooks/charmhelpers/contrib/openstack/amulet/utils.py'
160--- hooks/charmhelpers/contrib/openstack/amulet/utils.py 2015-10-22 13:22:39 +0000
161+++ hooks/charmhelpers/contrib/openstack/amulet/utils.py 2016-01-07 14:24:40 +0000
162@@ -18,6 +18,7 @@
163 import json
164 import logging
165 import os
166+import re
167 import six
168 import time
169 import urllib
170@@ -604,7 +605,22 @@
171 '{}'.format(sample_type, samples))
172 return None
173
174-# rabbitmq/amqp specific helpers:
175+ # rabbitmq/amqp specific helpers:
176+
177+ def rmq_wait_for_cluster(self, deployment, init_sleep=15, timeout=1200):
178+ """Wait for rmq units extended status to show cluster readiness,
179+ after an optional initial sleep period. Initial sleep is likely
180+ necessary to be effective following a config change, as status
181+ message may not instantly update to non-ready."""
182+
183+ if init_sleep:
184+ time.sleep(init_sleep)
185+
186+ message = re.compile('^Unit is ready and clustered$')
187+ deployment._auto_wait_for_status(message=message,
188+ timeout=timeout,
189+ include_only=['rabbitmq-server'])
190+
191 def add_rmq_test_user(self, sentry_units,
192 username="testuser1", password="changeme"):
193 """Add a test user via the first rmq juju unit, check connection as
194@@ -805,7 +821,10 @@
195 if port:
196 config['ssl_port'] = port
197
198- deployment.configure('rabbitmq-server', config)
199+ deployment.d.configure('rabbitmq-server', config)
200+
201+ # Wait for unit status
202+ self.rmq_wait_for_cluster(deployment)
203
204 # Confirm
205 tries = 0
206@@ -832,7 +851,10 @@
207
208 # Disable RMQ SSL
209 config = {'ssl': 'off'}
210- deployment.configure('rabbitmq-server', config)
211+ deployment.d.configure('rabbitmq-server', config)
212+
213+ # Wait for unit status
214+ self.rmq_wait_for_cluster(deployment)
215
216 # Confirm
217 tries = 0
218
219=== modified file 'hooks/charmhelpers/contrib/openstack/context.py'
220--- hooks/charmhelpers/contrib/openstack/context.py 2015-10-22 13:22:39 +0000
221+++ hooks/charmhelpers/contrib/openstack/context.py 2016-01-07 14:24:40 +0000
222@@ -952,6 +952,19 @@
223 'config': config}
224 return ovs_ctxt
225
226+ def midonet_ctxt(self):
227+ driver = neutron_plugin_attribute(self.plugin, 'driver',
228+ self.network_manager)
229+ midonet_config = neutron_plugin_attribute(self.plugin, 'config',
230+ self.network_manager)
231+ mido_ctxt = {'core_plugin': driver,
232+ 'neutron_plugin': 'midonet',
233+ 'neutron_security_groups': self.neutron_security_groups,
234+ 'local_ip': unit_private_ip(),
235+ 'config': midonet_config}
236+
237+ return mido_ctxt
238+
239 def __call__(self):
240 if self.network_manager not in ['quantum', 'neutron']:
241 return {}
242@@ -973,6 +986,8 @@
243 ctxt.update(self.nuage_ctxt())
244 elif self.plugin == 'plumgrid':
245 ctxt.update(self.pg_ctxt())
246+ elif self.plugin == 'midonet':
247+ ctxt.update(self.midonet_ctxt())
248
249 alchemy_flags = config('neutron-alchemy-flags')
250 if alchemy_flags:
251@@ -1105,7 +1120,7 @@
252
253 ctxt = {
254 ... other context ...
255- 'subordinate_config': {
256+ 'subordinate_configuration': {
257 'DEFAULT': {
258 'key1': 'value1',
259 },
260@@ -1146,22 +1161,23 @@
261 try:
262 sub_config = json.loads(sub_config)
263 except:
264- log('Could not parse JSON from subordinate_config '
265- 'setting from %s' % rid, level=ERROR)
266+ log('Could not parse JSON from '
267+ 'subordinate_configuration setting from %s'
268+ % rid, level=ERROR)
269 continue
270
271 for service in self.services:
272 if service not in sub_config:
273- log('Found subordinate_config on %s but it contained'
274- 'nothing for %s service' % (rid, service),
275- level=INFO)
276+ log('Found subordinate_configuration on %s but it '
277+ 'contained nothing for %s service'
278+ % (rid, service), level=INFO)
279 continue
280
281 sub_config = sub_config[service]
282 if self.config_file not in sub_config:
283- log('Found subordinate_config on %s but it contained'
284- 'nothing for %s' % (rid, self.config_file),
285- level=INFO)
286+ log('Found subordinate_configuration on %s but it '
287+ 'contained nothing for %s'
288+ % (rid, self.config_file), level=INFO)
289 continue
290
291 sub_config = sub_config[self.config_file]
292
293=== modified file 'hooks/charmhelpers/contrib/openstack/neutron.py'
294--- hooks/charmhelpers/contrib/openstack/neutron.py 2015-10-22 13:22:39 +0000
295+++ hooks/charmhelpers/contrib/openstack/neutron.py 2016-01-07 14:24:40 +0000
296@@ -209,6 +209,20 @@
297 'server_packages': ['neutron-server',
298 'neutron-plugin-plumgrid'],
299 'server_services': ['neutron-server']
300+ },
301+ 'midonet': {
302+ 'config': '/etc/neutron/plugins/midonet/midonet.ini',
303+ 'driver': 'midonet.neutron.plugin.MidonetPluginV2',
304+ 'contexts': [
305+ context.SharedDBContext(user=config('neutron-database-user'),
306+ database=config('neutron-database'),
307+ relation_prefix='neutron',
308+ ssl_dir=NEUTRON_CONF_DIR)],
309+ 'services': [],
310+ 'packages': [[headers_package()] + determine_dkms_package()],
311+ 'server_packages': ['neutron-server',
312+ 'python-neutron-plugin-midonet'],
313+ 'server_services': ['neutron-server']
314 }
315 }
316 if release >= 'icehouse':
317
318=== modified file 'hooks/charmhelpers/contrib/openstack/utils.py'
319--- hooks/charmhelpers/contrib/openstack/utils.py 2015-10-22 13:22:39 +0000
320+++ hooks/charmhelpers/contrib/openstack/utils.py 2016-01-07 14:24:40 +0000
321@@ -121,36 +121,37 @@
322 ('2.2.2', 'kilo'),
323 ('2.3.0', 'liberty'),
324 ('2.4.0', 'liberty'),
325+ ('2.5.0', 'liberty'),
326 ])
327
328 # >= Liberty version->codename mapping
329 PACKAGE_CODENAMES = {
330 'nova-common': OrderedDict([
331- ('12.0.0', 'liberty'),
332+ ('12.0', 'liberty'),
333 ]),
334 'neutron-common': OrderedDict([
335- ('7.0.0', 'liberty'),
336+ ('7.0', 'liberty'),
337 ]),
338 'cinder-common': OrderedDict([
339- ('7.0.0', 'liberty'),
340+ ('7.0', 'liberty'),
341 ]),
342 'keystone': OrderedDict([
343- ('8.0.0', 'liberty'),
344+ ('8.0', 'liberty'),
345 ]),
346 'horizon-common': OrderedDict([
347- ('8.0.0', 'liberty'),
348+ ('8.0', 'liberty'),
349 ]),
350 'ceilometer-common': OrderedDict([
351- ('5.0.0', 'liberty'),
352+ ('5.0', 'liberty'),
353 ]),
354 'heat-common': OrderedDict([
355- ('5.0.0', 'liberty'),
356+ ('5.0', 'liberty'),
357 ]),
358 'glance-common': OrderedDict([
359- ('11.0.0', 'liberty'),
360+ ('11.0', 'liberty'),
361 ]),
362 'openstack-dashboard': OrderedDict([
363- ('8.0.0', 'liberty'),
364+ ('8.0', 'liberty'),
365 ]),
366 }
367
368@@ -237,7 +238,14 @@
369 error_out(e)
370
371 vers = apt.upstream_version(pkg.current_ver.ver_str)
372- match = re.match('^(\d+)\.(\d+)\.(\d+)', vers)
373+ if 'swift' in pkg.name:
374+ # Fully x.y.z match for swift versions
375+ match = re.match('^(\d+)\.(\d+)\.(\d+)', vers)
376+ else:
377+ # x.y match only for 20XX.X
378+ # and ignore patch level for other packages
379+ match = re.match('^(\d+)\.(\d+)', vers)
380+
381 if match:
382 vers = match.group(0)
383
384@@ -249,13 +257,8 @@
385 # < Liberty co-ordinated project versions
386 try:
387 if 'swift' in pkg.name:
388- swift_vers = vers[:5]
389- if swift_vers not in SWIFT_CODENAMES:
390- # Deal with 1.10.0 upward
391- swift_vers = vers[:6]
392- return SWIFT_CODENAMES[swift_vers]
393+ return SWIFT_CODENAMES[vers]
394 else:
395- vers = vers[:6]
396 return OPENSTACK_CODENAMES[vers]
397 except KeyError:
398 if not fatal:
399@@ -858,7 +861,9 @@
400 if charm_state != 'active' and charm_state != 'unknown':
401 state = workload_state_compare(state, charm_state)
402 if message:
403- message = "{} {}".format(message, charm_message)
404+ charm_message = charm_message.replace("Incomplete relations: ",
405+ "")
406+ message = "{}, {}".format(message, charm_message)
407 else:
408 message = charm_message
409
410
411=== modified file 'hooks/charmhelpers/core/host.py'
412--- hooks/charmhelpers/core/host.py 2015-10-22 13:22:39 +0000
413+++ hooks/charmhelpers/core/host.py 2016-01-07 14:24:40 +0000
414@@ -566,7 +566,14 @@
415 os.chdir(cur)
416
417
418-def chownr(path, owner, group, follow_links=True):
419+def chownr(path, owner, group, follow_links=True, chowntopdir=False):
420+ """
421+ Recursively change user and group ownership of files and directories
422+ in given path. Doesn't chown path itself by default, only its children.
423+
424+ :param bool follow_links: Also Chown links if True
425+ :param bool chowntopdir: Also chown path itself if True
426+ """
427 uid = pwd.getpwnam(owner).pw_uid
428 gid = grp.getgrnam(group).gr_gid
429 if follow_links:
430@@ -574,6 +581,10 @@
431 else:
432 chown = os.lchown
433
434+ if chowntopdir:
435+ broken_symlink = os.path.lexists(path) and not os.path.exists(path)
436+ if not broken_symlink:
437+ chown(path, uid, gid)
438 for root, dirs, files in os.walk(path):
439 for name in dirs + files:
440 full = os.path.join(root, name)
441
442=== modified file 'hooks/charmhelpers/core/hugepage.py'
443--- hooks/charmhelpers/core/hugepage.py 2015-10-22 13:22:39 +0000
444+++ hooks/charmhelpers/core/hugepage.py 2016-01-07 14:24:40 +0000
445@@ -46,6 +46,8 @@
446 group_info = add_group(group)
447 gid = group_info.gr_gid
448 add_user_to_group(user, group)
449+ if max_map_count < 2 * nr_hugepages:
450+ max_map_count = 2 * nr_hugepages
451 sysctl_settings = {
452 'vm.nr_hugepages': nr_hugepages,
453 'vm.max_map_count': max_map_count,
454
455=== modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py'
456--- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-10-22 13:22:39 +0000
457+++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2016-01-07 14:24:40 +0000
458@@ -14,13 +14,18 @@
459 # You should have received a copy of the GNU Lesser General Public License
460 # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
461
462+import logging
463 import re
464+import sys
465 import six
466 from collections import OrderedDict
467 from charmhelpers.contrib.amulet.deployment import (
468 AmuletDeployment
469 )
470
471+DEBUG = logging.DEBUG
472+ERROR = logging.ERROR
473+
474
475 class OpenStackAmuletDeployment(AmuletDeployment):
476 """OpenStack amulet deployment.
477@@ -29,9 +34,12 @@
478 that is specifically for use by OpenStack charms.
479 """
480
481- def __init__(self, series=None, openstack=None, source=None, stable=True):
482+ def __init__(self, series=None, openstack=None, source=None,
483+ stable=True, log_level=DEBUG):
484 """Initialize the deployment environment."""
485 super(OpenStackAmuletDeployment, self).__init__(series)
486+ self.log = self.get_logger(level=log_level)
487+ self.log.info('OpenStackAmuletDeployment: init')
488 self.openstack = openstack
489 self.source = source
490 self.stable = stable
491@@ -39,6 +47,22 @@
492 # out.
493 self.current_next = "trusty"
494
495+ def get_logger(self, name="deployment-logger", level=logging.DEBUG):
496+ """Get a logger object that will log to stdout."""
497+ log = logging
498+ logger = log.getLogger(name)
499+ fmt = log.Formatter("%(asctime)s %(funcName)s "
500+ "%(levelname)s: %(message)s")
501+
502+ handler = log.StreamHandler(stream=sys.stdout)
503+ handler.setLevel(level)
504+ handler.setFormatter(fmt)
505+
506+ logger.addHandler(handler)
507+ logger.setLevel(level)
508+
509+ return logger
510+
511 def _determine_branch_locations(self, other_services):
512 """Determine the branch locations for the other services.
513
514@@ -46,6 +70,8 @@
515 stable or next (dev) branch, and based on this, use the corresonding
516 stable or next branches for the other_services."""
517
518+ self.log.info('OpenStackAmuletDeployment: determine branch locations')
519+
520 # Charms outside the lp:~openstack-charmers namespace
521 base_charms = ['mysql', 'mongodb', 'nrpe']
522
523@@ -83,6 +109,8 @@
524
525 def _add_services(self, this_service, other_services):
526 """Add services to the deployment and set openstack-origin/source."""
527+ self.log.info('OpenStackAmuletDeployment: adding services')
528+
529 other_services = self._determine_branch_locations(other_services)
530
531 super(OpenStackAmuletDeployment, self)._add_services(this_service,
532@@ -112,11 +140,12 @@
533
534 def _configure_services(self, configs):
535 """Configure all of the services."""
536+ self.log.info('OpenStackAmuletDeployment: configure services')
537 for service, config in six.iteritems(configs):
538 self.d.configure(service, config)
539
540 def _auto_wait_for_status(self, message=None, exclude_services=None,
541- timeout=1800):
542+ include_only=None, timeout=1800):
543 """Wait for all units to have a specific extended status, except
544 for any defined as excluded. Unless specified via message, any
545 status containing any case of 'ready' will be considered a match.
546@@ -127,7 +156,7 @@
547 message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE)
548
549 Wait for all units to reach this status (exact match):
550- message = 'Unit is ready'
551+ message = re.compile('^Unit is ready and clustered$')
552
553 Wait for all units to reach any one of these (exact match):
554 message = re.compile('Unit is ready|OK|Ready')
555@@ -139,20 +168,50 @@
556 https://github.com/juju/amulet/blob/master/amulet/sentry.py
557
558 :param message: Expected status match
559- :param exclude_services: List of juju service names to ignore
560+ :param exclude_services: List of juju service names to ignore,
561+ not to be used in conjuction with include_only.
562+ :param include_only: List of juju service names to exclusively check,
563+ not to be used in conjuction with exclude_services.
564 :param timeout: Maximum time in seconds to wait for status match
565 :returns: None. Raises if timeout is hit.
566 """
567-
568- if not message:
569+ self.log.info('Waiting for extended status on units...')
570+
571+ all_services = self.d.services.keys()
572+
573+ if exclude_services and include_only:
574+ raise ValueError('exclude_services can not be used '
575+ 'with include_only')
576+
577+ if message:
578+ if isinstance(message, re._pattern_type):
579+ match = message.pattern
580+ else:
581+ match = message
582+
583+ self.log.debug('Custom extended status wait match: '
584+ '{}'.format(match))
585+ else:
586+ self.log.debug('Default extended status wait match: contains '
587+ 'READY (case-insensitive)')
588 message = re.compile('.*ready.*', re.IGNORECASE)
589
590- if not exclude_services:
591+ if exclude_services:
592+ self.log.debug('Excluding services from extended status match: '
593+ '{}'.format(exclude_services))
594+ else:
595 exclude_services = []
596
597- services = list(set(self.d.services.keys()) - set(exclude_services))
598+ if include_only:
599+ services = include_only
600+ else:
601+ services = list(set(all_services) - set(exclude_services))
602+
603+ self.log.debug('Waiting up to {}s for extended status on services: '
604+ '{}'.format(timeout, services))
605 service_messages = {service: message for service in services}
606 self.d.sentry.wait_for_messages(service_messages, timeout=timeout)
607+ self.log.info('OK')
608
609 def _get_openstack_release(self):
610 """Get openstack release.
611
612=== modified file 'tests/charmhelpers/contrib/openstack/amulet/utils.py'
613--- tests/charmhelpers/contrib/openstack/amulet/utils.py 2015-10-22 13:22:39 +0000
614+++ tests/charmhelpers/contrib/openstack/amulet/utils.py 2016-01-07 14:24:40 +0000
615@@ -18,6 +18,7 @@
616 import json
617 import logging
618 import os
619+import re
620 import six
621 import time
622 import urllib
623@@ -604,7 +605,22 @@
624 '{}'.format(sample_type, samples))
625 return None
626
627-# rabbitmq/amqp specific helpers:
628+ # rabbitmq/amqp specific helpers:
629+
630+ def rmq_wait_for_cluster(self, deployment, init_sleep=15, timeout=1200):
631+ """Wait for rmq units extended status to show cluster readiness,
632+ after an optional initial sleep period. Initial sleep is likely
633+ necessary to be effective following a config change, as status
634+ message may not instantly update to non-ready."""
635+
636+ if init_sleep:
637+ time.sleep(init_sleep)
638+
639+ message = re.compile('^Unit is ready and clustered$')
640+ deployment._auto_wait_for_status(message=message,
641+ timeout=timeout,
642+ include_only=['rabbitmq-server'])
643+
644 def add_rmq_test_user(self, sentry_units,
645 username="testuser1", password="changeme"):
646 """Add a test user via the first rmq juju unit, check connection as
647@@ -805,7 +821,10 @@
648 if port:
649 config['ssl_port'] = port
650
651- deployment.configure('rabbitmq-server', config)
652+ deployment.d.configure('rabbitmq-server', config)
653+
654+ # Wait for unit status
655+ self.rmq_wait_for_cluster(deployment)
656
657 # Confirm
658 tries = 0
659@@ -832,7 +851,10 @@
660
661 # Disable RMQ SSL
662 config = {'ssl': 'off'}
663- deployment.configure('rabbitmq-server', config)
664+ deployment.d.configure('rabbitmq-server', config)
665+
666+ # Wait for unit status
667+ self.rmq_wait_for_cluster(deployment)
668
669 # Confirm
670 tries = 0

Subscribers

People subscribed via source and target branches