Merge lp:~1chb1n/charms/trusty/nova-compute/next-amulet-15.10 into lp:~openstack-charmers-archive/charms/trusty/nova-compute/next

Proposed by Ryan Beisner
Status: Merged
Merged at revision: 175
Proposed branch: lp:~1chb1n/charms/trusty/nova-compute/next-amulet-15.10
Merge into: lp:~openstack-charmers-archive/charms/trusty/nova-compute/next
Diff against target: 632 lines (+249/-120)
5 files modified
tests/020-basic-trusty-liberty (+11/-0)
tests/021-basic-wily-liberty (+9/-0)
tests/README (+10/-0)
tests/basic_deployment.py (+179/-120)
tests/charmhelpers/contrib/openstack/amulet/deployment.py (+40/-0)
To merge this branch: bzr merge lp:~1chb1n/charms/trusty/nova-compute/next-amulet-15.10
Reviewer Review Type Date Requested Status
Liam Young (community) Approve
Review via email: mp+274659@code.launchpad.net

Description of the change

Update amulet tests for Vivid-Kilo, Trusty-Liberty, prep for Wily-Liberty.

Add new logic to wait for extended status message to confirm deploy is ready, before testing.

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

charm_lint_check #12019 nova-compute-next for 1chb1n mp274659
    LINT OK: passed

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

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

charm_unit_test #11170 nova-compute-next for 1chb1n mp274659
    UNIT OK: passed

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

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

charm_amulet_test #7381 nova-compute-next for 1chb1n mp274659
    AMULET OK: passed

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

Revision history for this message
Liam Young (gnuoy) wrote :

Approve

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/019-basic-vivid-kilo' (properties changed: -x to +x)
2=== added file 'tests/020-basic-trusty-liberty'
3--- tests/020-basic-trusty-liberty 1970-01-01 00:00:00 +0000
4+++ tests/020-basic-trusty-liberty 2015-10-16 05:42:31 +0000
5@@ -0,0 +1,11 @@
6+#!/usr/bin/python
7+
8+"""Amulet tests on a basic nova compute deployment on trusty-liberty."""
9+
10+from basic_deployment import NovaBasicDeployment
11+
12+if __name__ == '__main__':
13+ deployment = NovaBasicDeployment(series='trusty',
14+ openstack='cloud:trusty-liberty',
15+ source='cloud:trusty-updates/liberty')
16+ deployment.run_tests()
17
18=== added file 'tests/021-basic-wily-liberty'
19--- tests/021-basic-wily-liberty 1970-01-01 00:00:00 +0000
20+++ tests/021-basic-wily-liberty 2015-10-16 05:42:31 +0000
21@@ -0,0 +1,9 @@
22+#!/usr/bin/python
23+
24+"""Amulet tests on a basic nova compute deployment on wily-liberty."""
25+
26+from basic_deployment import NovaBasicDeployment
27+
28+if __name__ == '__main__':
29+ deployment = NovaBasicDeployment(series='wily')
30+ deployment.run_tests()
31
32=== modified file 'tests/README'
33--- tests/README 2014-09-30 20:31:36 +0000
34+++ tests/README 2015-10-16 05:42:31 +0000
35@@ -1,6 +1,16 @@
36 This directory provides Amulet tests that focus on verification of nova-compute
37 deployments.
38
39+test_* methods are called in lexical sort order, although each individual test
40+should be idempotent, and expected to pass regardless of run order.
41+
42+Test name convention to ensure desired test order:
43+ 1xx service and endpoint checks
44+ 2xx relation checks
45+ 3xx config checks
46+ 4xx functional checks
47+ 9xx restarts and other final checks
48+
49 In order to run tests, you'll need charm-tools installed (in addition to
50 juju, of course):
51 sudo add-apt-repository ppa:juju/stable
52
53=== modified file 'tests/basic_deployment.py'
54--- tests/basic_deployment.py 2015-10-02 09:40:38 +0000
55+++ tests/basic_deployment.py 2015-10-16 05:42:31 +0000
56@@ -1,8 +1,5 @@
57-#!/usr/bin/python
58-
59 import amulet
60 import os
61-import time
62 import yaml
63
64 from charmhelpers.contrib.openstack.amulet.deployment import (
65@@ -11,8 +8,8 @@
66
67 from charmhelpers.contrib.openstack.amulet.utils import (
68 OpenStackAmuletUtils,
69- DEBUG, # flake8: noqa
70- ERROR
71+ DEBUG,
72+ # ERROR
73 )
74
75 # Use DEBUG to turn on debug logging
76@@ -22,15 +19,21 @@
77 class NovaBasicDeployment(OpenStackAmuletDeployment):
78 """Amulet tests on a basic nova compute deployment."""
79
80- def __init__(self, series=None, openstack=None, source=None, git=False,
81- stable=False):
82+ def __init__(self, series=None, openstack=None, source=None,
83+ git=False, stable=False):
84 """Deploy the entire test environment."""
85- super(NovaBasicDeployment, self).__init__(series, openstack, source, stable)
86+ super(NovaBasicDeployment, self).__init__(series, openstack,
87+ source, stable)
88 self.git = git
89 self._add_services()
90 self._add_relations()
91 self._configure_services()
92 self._deploy()
93+
94+ u.log.info('Waiting on extended status checks...')
95+ exclude_services = ['mysql']
96+ self._auto_wait_for_status(exclude_services=exclude_services)
97+
98 self._initialize_tests()
99
100 def _add_services(self):
101@@ -41,8 +44,10 @@
102 compatible with the local charm (e.g. stable or next).
103 """
104 this_service = {'name': 'nova-compute'}
105- other_services = [{'name': 'mysql'}, {'name': 'rabbitmq-server'},
106- {'name': 'nova-cloud-controller'}, {'name': 'keystone'},
107+ other_services = [{'name': 'mysql'},
108+ {'name': 'rabbitmq-server'},
109+ {'name': 'nova-cloud-controller'},
110+ {'name': 'keystone'},
111 {'name': 'glance'}]
112 super(NovaBasicDeployment, self)._add_services(this_service,
113 other_services)
114@@ -50,18 +55,20 @@
115 def _add_relations(self):
116 """Add all of the relations for the services."""
117 relations = {
118- 'nova-compute:image-service': 'glance:image-service',
119- 'nova-compute:shared-db': 'mysql:shared-db',
120- 'nova-compute:amqp': 'rabbitmq-server:amqp',
121- 'nova-cloud-controller:shared-db': 'mysql:shared-db',
122- 'nova-cloud-controller:identity-service': 'keystone:identity-service',
123- 'nova-cloud-controller:amqp': 'rabbitmq-server:amqp',
124- 'nova-cloud-controller:cloud-compute': 'nova-compute:cloud-compute',
125- 'nova-cloud-controller:image-service': 'glance:image-service',
126- 'keystone:shared-db': 'mysql:shared-db',
127- 'glance:identity-service': 'keystone:identity-service',
128- 'glance:shared-db': 'mysql:shared-db',
129- 'glance:amqp': 'rabbitmq-server:amqp'
130+ 'nova-compute:image-service': 'glance:image-service',
131+ 'nova-compute:shared-db': 'mysql:shared-db',
132+ 'nova-compute:amqp': 'rabbitmq-server:amqp',
133+ 'nova-cloud-controller:shared-db': 'mysql:shared-db',
134+ 'nova-cloud-controller:identity-service': 'keystone:'
135+ 'identity-service',
136+ 'nova-cloud-controller:amqp': 'rabbitmq-server:amqp',
137+ 'nova-cloud-controller:cloud-compute': 'nova-compute:'
138+ 'cloud-compute',
139+ 'nova-cloud-controller:image-service': 'glance:image-service',
140+ 'keystone:shared-db': 'mysql:shared-db',
141+ 'glance:identity-service': 'keystone:identity-service',
142+ 'glance:shared-db': 'mysql:shared-db',
143+ 'glance:amqp': 'rabbitmq-server:amqp'
144 }
145 super(NovaBasicDeployment, self)._add_relations(relations)
146
147@@ -86,7 +93,7 @@
148 openstack_origin_git = {
149 'repositories': [
150 {'name': 'requirements',
151- 'repository': reqs_repo,
152+ 'repository': reqs_repo,
153 'branch': branch},
154 {'name': 'neutron',
155 'repository': neutron_repo,
156@@ -99,8 +106,11 @@
157 'http_proxy': amulet_http_proxy,
158 'https_proxy': amulet_http_proxy,
159 }
160- nova_config['openstack-origin-git'] = yaml.dump(openstack_origin_git)
161- nova_cc_config['openstack-origin-git'] = yaml.dump(openstack_origin_git)
162+ nova_config['openstack-origin-git'] = \
163+ yaml.dump(openstack_origin_git)
164+
165+ nova_cc_config['openstack-origin-git'] = \
166+ yaml.dump(openstack_origin_git)
167
168 keystone_config = {'admin-password': 'openstack',
169 'admin-token': 'ubuntutesting'}
170@@ -118,8 +128,10 @@
171 self.nova_cc_sentry = self.d.sentry.unit['nova-cloud-controller/0']
172 self.glance_sentry = self.d.sentry.unit['glance/0']
173
174- # Let things settle a bit before moving forward
175- time.sleep(30)
176+ u.log.debug('openstack release val: {}'.format(
177+ self._get_openstack_release()))
178+ u.log.debug('openstack release str: {}'.format(
179+ self._get_openstack_release_string()))
180
181 # Authenticate admin with keystone
182 self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
183@@ -156,32 +168,38 @@
184 password='password',
185 tenant=self.demo_tenant)
186
187- def test_services(self):
188+ def test_100_services(self):
189 """Verify the expected services are running on the corresponding
190 service units."""
191- commands = {
192- self.mysql_sentry: ['status mysql'],
193- self.rabbitmq_sentry: ['sudo service rabbitmq-server status'],
194- self.nova_compute_sentry: ['status nova-compute',
195- 'status nova-network',
196- 'status nova-api'],
197- self.nova_cc_sentry: ['status nova-api-ec2',
198- 'status nova-api-os-compute',
199- 'status nova-objectstore',
200- 'status nova-cert',
201- 'status nova-scheduler'],
202- self.keystone_sentry: ['status keystone'],
203- self.glance_sentry: ['status glance-registry', 'status glance-api']
204+ u.log.debug('Checking system services on units...')
205+
206+ services = {
207+ self.mysql_sentry: ['mysql'],
208+ self.rabbitmq_sentry: ['rabbitmq-server'],
209+ self.nova_compute_sentry: ['nova-compute',
210+ 'nova-network',
211+ 'nova-api'],
212+ self.nova_cc_sentry: ['nova-api-ec2',
213+ 'nova-api-os-compute',
214+ 'nova-objectstore',
215+ 'nova-cert',
216+ 'nova-scheduler'],
217+ self.keystone_sentry: ['keystone'],
218+ self.glance_sentry: ['glance-registry',
219+ 'glance-api']
220 }
221+
222 if self._get_openstack_release() >= self.precise_grizzly:
223- commands[self.nova_cc_sentry] = ['status nova-conductor']
224+ services[self.nova_cc_sentry] = ['nova-conductor']
225
226- ret = u.validate_services(commands)
227+ ret = u.validate_services_by_name(services)
228 if ret:
229 amulet.raise_status(amulet.FAIL, msg=ret)
230
231- def test_service_catalog(self):
232+ def test_102_service_catalog(self):
233 """Verify that the service catalog endpoint data is valid."""
234+ u.log.debug('Checking keystone service catalog...')
235+
236 endpoint_vol = {'adminURL': u.valid_url,
237 'region': 'RegionOne',
238 'publicURL': u.valid_url,
239@@ -190,9 +208,11 @@
240 'region': 'RegionOne',
241 'publicURL': u.valid_url,
242 'internalURL': u.valid_url}
243+
244 if self._get_openstack_release() >= self.precise_folsom:
245 endpoint_vol['id'] = u.not_null
246 endpoint_id['id'] = u.not_null
247+
248 if self._get_openstack_release() >= self.trusty_kilo:
249 expected = {'compute': [endpoint_vol], 'identity': [endpoint_id]}
250 else:
251@@ -204,16 +224,20 @@
252 if ret:
253 amulet.raise_status(amulet.FAIL, msg=ret)
254
255- def test_openstack_compute_api_endpoint(self):
256+ def test_104_openstack_compute_api_endpoint(self):
257 """Verify the openstack compute api (osapi) endpoint data."""
258+ u.log.debug('Checking compute endpoint data...')
259+
260 endpoints = self.keystone.endpoints.list()
261 admin_port = internal_port = public_port = '8774'
262- expected = {'id': u.not_null,
263- 'region': 'RegionOne',
264- 'adminurl': u.valid_url,
265- 'internalurl': u.valid_url,
266- 'publicurl': u.valid_url,
267- 'service_id': u.not_null}
268+ expected = {
269+ 'id': u.not_null,
270+ 'region': 'RegionOne',
271+ 'adminurl': u.valid_url,
272+ 'internalurl': u.valid_url,
273+ 'publicurl': u.valid_url,
274+ 'service_id': u.not_null
275+ }
276
277 ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
278 public_port, expected)
279@@ -221,19 +245,22 @@
280 message = 'osapi endpoint: {}'.format(ret)
281 amulet.raise_status(amulet.FAIL, msg=message)
282
283- def test_ec2_api_endpoint(self):
284+ def test_106_ec2_api_endpoint(self):
285 """Verify the EC2 api endpoint data."""
286 if self._get_openstack_release() >= self.trusty_kilo:
287 return
288
289+ u.log.debug('Checking ec2 endpoint data...')
290 endpoints = self.keystone.endpoints.list()
291 admin_port = internal_port = public_port = '8773'
292- expected = {'id': u.not_null,
293- 'region': 'RegionOne',
294- 'adminurl': u.valid_url,
295- 'internalurl': u.valid_url,
296- 'publicurl': u.valid_url,
297- 'service_id': u.not_null}
298+ expected = {
299+ 'id': u.not_null,
300+ 'region': 'RegionOne',
301+ 'adminurl': u.valid_url,
302+ 'internalurl': u.valid_url,
303+ 'publicurl': u.valid_url,
304+ 'service_id': u.not_null
305+ }
306
307 ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
308 public_port, expected)
309@@ -241,19 +268,22 @@
310 message = 'EC2 endpoint: {}'.format(ret)
311 amulet.raise_status(amulet.FAIL, msg=message)
312
313- def test_s3_api_endpoint(self):
314+ def test_108_s3_api_endpoint(self):
315 """Verify the S3 api endpoint data."""
316 if self._get_openstack_release() >= self.trusty_kilo:
317 return
318
319+ u.log.debug('Checking s3 endpoint data...')
320 endpoints = self.keystone.endpoints.list()
321 admin_port = internal_port = public_port = '3333'
322- expected = {'id': u.not_null,
323- 'region': 'RegionOne',
324- 'adminurl': u.valid_url,
325- 'internalurl': u.valid_url,
326- 'publicurl': u.valid_url,
327- 'service_id': u.not_null}
328+ expected = {
329+ 'id': u.not_null,
330+ 'region': 'RegionOne',
331+ 'adminurl': u.valid_url,
332+ 'internalurl': u.valid_url,
333+ 'publicurl': u.valid_url,
334+ 'service_id': u.not_null
335+ }
336
337 ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
338 public_port, expected)
339@@ -261,8 +291,10 @@
340 message = 'S3 endpoint: {}'.format(ret)
341 amulet.raise_status(amulet.FAIL, msg=message)
342
343- def test_nova_shared_db_relation(self):
344+ def test_200_nova_shared_db_relation(self):
345 """Verify the nova-compute to mysql shared-db relation data"""
346+ u.log.debug('Checking n-c:mysql db relation data...')
347+
348 unit = self.nova_compute_sentry
349 relation = ['shared-db', 'mysql:shared-db']
350 expected = {
351@@ -277,8 +309,9 @@
352 message = u.relation_error('nova-compute shared-db', ret)
353 amulet.raise_status(amulet.FAIL, msg=message)
354
355- def test_mysql_shared_db_relation(self):
356+ def test_202_mysql_shared_db_relation(self):
357 """Verify the mysql to nova-compute shared-db relation data"""
358+ u.log.debug('Checking mysql:n-c db relation data...')
359 unit = self.mysql_sentry
360 relation = ['shared-db', 'nova-compute:shared-db']
361 expected = {
362@@ -292,8 +325,9 @@
363 message = u.relation_error('mysql shared-db', ret)
364 amulet.raise_status(amulet.FAIL, msg=message)
365
366- def test_nova_amqp_relation(self):
367+ def test_204_nova_amqp_relation(self):
368 """Verify the nova-compute to rabbitmq-server amqp relation data"""
369+ u.log.debug('Checking n-c:rmq amqp relation data...')
370 unit = self.nova_compute_sentry
371 relation = ['amqp', 'rabbitmq-server:amqp']
372 expected = {
373@@ -307,8 +341,9 @@
374 message = u.relation_error('nova-compute amqp', ret)
375 amulet.raise_status(amulet.FAIL, msg=message)
376
377- def test_rabbitmq_amqp_relation(self):
378+ def test_206_rabbitmq_amqp_relation(self):
379 """Verify the rabbitmq-server to nova-compute amqp relation data"""
380+ u.log.debug('Checking rmq:n-c amqp relation data...')
381 unit = self.rabbitmq_sentry
382 relation = ['amqp', 'nova-compute:amqp']
383 expected = {
384@@ -322,8 +357,9 @@
385 message = u.relation_error('rabbitmq amqp', ret)
386 amulet.raise_status(amulet.FAIL, msg=message)
387
388- def test_nova_cloud_compute_relation(self):
389+ def test_208_nova_cloud_compute_relation(self):
390 """Verify the nova-compute to nova-cc cloud-compute relation data"""
391+ u.log.debug('Checking n-c:n-c-c cloud-compute relation data...')
392 unit = self.nova_compute_sentry
393 relation = ['cloud-compute', 'nova-cloud-controller:cloud-compute']
394 expected = {
395@@ -335,8 +371,9 @@
396 message = u.relation_error('nova-compute cloud-compute', ret)
397 amulet.raise_status(amulet.FAIL, msg=message)
398
399- def test_nova_cc_cloud_compute_relation(self):
400+ def test_210_nova_cc_cloud_compute_relation(self):
401 """Verify the nova-cc to nova-compute cloud-compute relation data"""
402+ u.log.debug('Checking n-c-c:n-c cloud-compute relation data...')
403 unit = self.nova_cc_sentry
404 relation = ['cloud-compute', 'nova-compute:cloud-compute']
405 expected = {
406@@ -354,52 +391,25 @@
407 message = u.relation_error('nova-cc cloud-compute', ret)
408 amulet.raise_status(amulet.FAIL, msg=message)
409
410- def test_z_restart_on_config_change(self):
411- """Verify that the specified services are restarted when the config
412- is changed.
413-
414- Note(coreycb): The method name with the _z_ is a little odd
415- but it forces the test to run last. It just makes things
416- easier because restarting services requires re-authorization.
417- """
418- # NOTE(coreycb): Skipping failing test on essex until resolved.
419- # config-flags don't take effect on essex.
420- if self._get_openstack_release() == self.precise_essex:
421- u.log.error("Skipping failing test until resolved")
422- return
423-
424- services = ['nova-compute', 'nova-api', 'nova-network']
425- self.d.configure('nova-compute', {'config-flags': 'verbose=False'})
426-
427- time = 20
428- for s in services:
429- if not u.service_restarted(self.nova_compute_sentry, s,
430- '/etc/nova/nova.conf', sleep_time=time):
431- self.d.configure('nova-compute', {'config-flags': 'verbose=True'})
432- msg = "service {} didn't restart after config change".format(s)
433- amulet.raise_status(amulet.FAIL, msg=msg)
434- time = 0
435-
436- self.d.configure('nova-compute', {'config-flags': 'verbose=True'})
437-
438- def test_nova_config(self):
439+ def test_300_nova_config(self):
440 """Verify the data in the nova config file."""
441 # NOTE(coreycb): Currently no way to test on essex because config file
442 # has no section headers.
443 if self._get_openstack_release() == self.precise_essex:
444 return
445
446+ u.log.debug('Checking nova config file data...')
447 unit = self.nova_compute_sentry
448 conf = '/etc/nova/nova.conf'
449- rabbitmq_relation = self.rabbitmq_sentry.relation('amqp',
450- 'nova-compute:amqp')
451- glance_relation = self.glance_sentry.relation('image-service',
452- 'nova-compute:image-service')
453- mysql_relation = self.mysql_sentry.relation('shared-db',
454- 'nova-compute:shared-db')
455+ rmq_nc_rel = self.rabbitmq_sentry.relation('amqp',
456+ 'nova-compute:amqp')
457+ gl_nc_rel = self.glance_sentry.relation('image-service',
458+ 'nova-compute:image-service')
459+ db_nc_rel = self.mysql_sentry.relation('shared-db',
460+ 'nova-compute:shared-db')
461 db_uri = "mysql://{}:{}@{}/{}".format('nova',
462- mysql_relation['nova_password'],
463- mysql_relation['db_host'],
464+ db_nc_rel['nova_password'],
465+ db_nc_rel['db_host'],
466 'nova')
467
468 expected = {
469@@ -428,9 +438,9 @@
470 expected[d]['sql_connection'] = db_uri
471 expected[d]['rabbit_userid'] = 'nova'
472 expected[d]['rabbit_virtual_host'] = 'openstack'
473- expected[d]['rabbit_password'] = rabbitmq_relation['password']
474- expected[d]['rabbit_host'] = rabbitmq_relation['hostname']
475- expected[d]['glance_api_servers'] = glance_relation['glance-api-server']
476+ expected[d]['rabbit_password'] = rmq_nc_rel['password']
477+ expected[d]['rabbit_host'] = rmq_nc_rel['hostname']
478+ expected[d]['glance_api_servers'] = gl_nc_rel['glance-api-server']
479 else:
480 oslo_concurrency = {
481 'oslo_concurrency': {
482@@ -446,13 +456,13 @@
483 'oslo_messaging_rabbit': {
484 'rabbit_userid': 'nova',
485 'rabbit_virtual_host': 'openstack',
486- 'rabbit_password': rabbitmq_relation['password'],
487- 'rabbit_host': rabbitmq_relation['hostname'],
488+ 'rabbit_password': rmq_nc_rel['password'],
489+ 'rabbit_host': rmq_nc_rel['hostname'],
490 }
491 }
492 glance = {
493 'glance': {
494- 'api_servers': glance_relation['glance-api-server']
495+ 'api_servers': gl_nc_rel['glance-api-server']
496 }
497 }
498 expected.update(oslo_concurrency)
499@@ -466,15 +476,17 @@
500 message = "nova config error: {}".format(ret)
501 amulet.raise_status(amulet.FAIL, msg=message)
502
503- def test_image_instance_create(self):
504+ def test_400_image_instance_create(self):
505 """Create an image/instance, verify they exist, and delete them."""
506 # NOTE(coreycb): Skipping failing test on essex until resolved. essex
507- # nova API calls are getting "Malformed request url (HTTP
508- # 400)".
509+ # nova API calls are getting "Malformed request url
510+ # (HTTP 400)".
511 if self._get_openstack_release() == self.precise_essex:
512- u.log.error("Skipping failing test until resolved")
513+ u.log.error("Skipping test (due to Essex)")
514 return
515
516+ u.log.debug('Checking nova instance creation...')
517+
518 image = u.create_cirros_image(self.glance, "cirros-image")
519 if not image:
520 amulet.raise_status(amulet.FAIL, msg="Image create failed")
521@@ -496,5 +508,52 @@
522 message = "nova cirros instance does not exist"
523 amulet.raise_status(amulet.FAIL, msg=message)
524
525- u.delete_image(self.glance, image)
526- u.delete_instance(self.nova_demo, instance)
527+ u.delete_resource(self.glance.images, image.id,
528+ msg="glance image")
529+
530+ u.delete_resource(self.nova_demo.servers, instance.id,
531+ msg="nova instance")
532+
533+ def test_900_restart_on_config_change(self):
534+ """Verify that the specified services are restarted when the config
535+ is changed."""
536+ # NOTE(coreycb): Skipping failing test on essex until resolved.
537+ # config-flags don't take effect on essex.
538+ if self._get_openstack_release() == self.precise_essex:
539+ u.log.error("Skipping failing test until resolved")
540+ return
541+
542+ sentry = self.nova_compute_sentry
543+ juju_service = 'nova-compute'
544+
545+ # Expected default and alternate values
546+ set_default = {'verbose': 'False'}
547+ set_alternate = {'verbose': 'True'}
548+
549+ # Services which are expected to restart upon config change,
550+ # and corresponding config files affected by the change
551+ conf_file = '/etc/nova/nova.conf'
552+ services = {
553+ 'nova-compute': conf_file,
554+ 'nova-api': conf_file,
555+ 'nova-network': conf_file
556+ }
557+
558+ # Make config change, check for service restarts
559+ u.log.debug('Making config change on {}...'.format(juju_service))
560+ mtime = u.get_sentry_time(sentry)
561+ self.d.configure(juju_service, set_alternate)
562+
563+ sleep_time = 30
564+ for s, conf_file in services.iteritems():
565+ u.log.debug("Checking that service restarted: {}".format(s))
566+ if not u.validate_service_config_changed(sentry, mtime, s,
567+ conf_file,
568+ sleep_time=sleep_time):
569+
570+ self.d.configure(juju_service, set_default)
571+ msg = "service {} didn't restart after config change".format(s)
572+ amulet.raise_status(amulet.FAIL, msg=msg)
573+ sleep_time = 0
574+
575+ self.d.configure(juju_service, set_default)
576
577=== modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py'
578--- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-09-28 07:56:16 +0000
579+++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2015-10-16 05:42:31 +0000
580@@ -14,6 +14,7 @@
581 # You should have received a copy of the GNU Lesser General Public License
582 # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
583
584+import re
585 import six
586 from collections import OrderedDict
587 from charmhelpers.contrib.amulet.deployment import (
588@@ -114,6 +115,45 @@
589 for service, config in six.iteritems(configs):
590 self.d.configure(service, config)
591
592+ def _auto_wait_for_status(self, message=None, exclude_services=None,
593+ timeout=1800):
594+ """Wait for all units to have a specific extended status, except
595+ for any defined as excluded. Unless specified via message, any
596+ status containing any case of 'ready' will be considered a match.
597+
598+ Examples of message usage:
599+
600+ Wait for all unit status to CONTAIN any case of 'ready' or 'ok':
601+ message = re.compile('.*ready.*|.*ok.*', re.IGNORECASE)
602+
603+ Wait for all units to reach this status (exact match):
604+ message = 'Unit is ready'
605+
606+ Wait for all units to reach any one of these (exact match):
607+ message = re.compile('Unit is ready|OK|Ready')
608+
609+ Wait for at least one unit to reach this status (exact match):
610+ message = {'ready'}
611+
612+ See Amulet's sentry.wait_for_messages() for message usage detail.
613+ https://github.com/juju/amulet/blob/master/amulet/sentry.py
614+
615+ :param message: Expected status match
616+ :param exclude_services: List of juju service names to ignore
617+ :param timeout: Maximum time in seconds to wait for status match
618+ :returns: None. Raises if timeout is hit.
619+ """
620+
621+ if not message:
622+ message = re.compile('.*ready.*', re.IGNORECASE)
623+
624+ if not exclude_services:
625+ exclude_services = []
626+
627+ services = list(set(self.d.services.keys()) - set(exclude_services))
628+ service_messages = {service: message for service in services}
629+ self.d.sentry.wait_for_messages(service_messages, timeout=timeout)
630+
631 def _get_openstack_release(self):
632 """Get openstack release.
633

Subscribers

People subscribed via source and target branches