Merge lp:~1chb1n/charms/trusty/neutron-api/next-amulet-update into lp:~openstack-charmers-archive/charms/trusty/neutron-api/next

Proposed by Ryan Beisner
Status: Merged
Merged at revision: 139
Proposed branch: lp:~1chb1n/charms/trusty/neutron-api/next-amulet-update
Merge into: lp:~openstack-charmers-archive/charms/trusty/neutron-api/next
Diff against target: 965 lines (+362/-232)
8 files modified
Makefile (+11/-12)
tests/00-setup (+7/-3)
tests/020-basic-trusty-liberty (+11/-0)
tests/021-basic-wily-liberty (+9/-0)
tests/README (+33/-0)
tests/basic_deployment.py (+171/-164)
tests/charmhelpers/contrib/amulet/utils.py (+101/-53)
tests/tests.yaml (+19/-0)
To merge this branch: bzr merge lp:~1chb1n/charms/trusty/neutron-api/next-amulet-update
Reviewer Review Type Date Requested Status
Billy Olsen Pending
OpenStack Charmers Pending
James Page Pending
Corey Bryant Pending
Review via email: mp+269302@code.launchpad.net

This proposal supersedes a proposal from 2015-07-01.

Description of the change

Update amulet tests for T-K, V-K, prep for T-L and W-L. Resolve bug 1474030 race in svc restart checks.

This merge proposal is dependent on the corresponding charm-helpers changes landing:
https://code.launchpad.net/~1chb1n/charm-helpers/amulet-svc-restart-race

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

charm_lint_check #5719 neutron-api-next for 1chb1n mp263594
    LINT OK: passed

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

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_unit_test #5351 neutron-api-next for 1chb1n mp263594
    UNIT OK: passed

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

Revision history for this message
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal

charm_amulet_test #4911 neutron-api-next for 1chb1n mp263594
    AMULET FAIL: amulet-test failed

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

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

Revision history for this message
James Page (james-page) : Posted in a previous version of this proposal
review: Needs Resubmitting
Revision history for this message
Ryan Beisner (1chb1n) wrote : Posted in a previous version of this proposal

FYI - There is really an underlying race in the logic in c-h, causing the most recent amulet fail above. https://launchpad.net/bugs/1474030

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

charm_lint_check #8818 neutron-api-next for 1chb1n mp269302
    LINT OK: passed

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

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

charm_unit_test #8146 neutron-api-next for 1chb1n mp269302
    UNIT OK: passed

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

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

charm_amulet_test #6050 neutron-api-next for 1chb1n mp269302
    AMULET FAIL: amulet-test failed

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

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

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

charm_lint_check #8829 neutron-api-next for 1chb1n mp269302
    LINT OK: passed

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

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

charm_unit_test #8157 neutron-api-next for 1chb1n mp269302
    UNIT OK: passed

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

125. By Ryan Beisner

resync tests/charmhelpers for updated svc restarted checks

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

charm_lint_check #8831 neutron-api-next for 1chb1n mp269302
    LINT OK: passed

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

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

charm_unit_test #8159 neutron-api-next for 1chb1n mp269302
    UNIT OK: passed

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

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

charm_amulet_test #6065 neutron-api-next for 1chb1n mp269302
    AMULET OK: passed

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

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

charm_amulet_test #6074 neutron-api-next for 1chb1n mp269302
    AMULET OK: passed

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

126. By Ryan Beisner

resync tests/charmhelpers

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

charm_lint_check #9223 neutron-api-next for 1chb1n mp269302
    LINT OK: passed

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

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

charm_unit_test #8525 neutron-api-next for 1chb1n mp269302
    UNIT OK: passed

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

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

charm_amulet_test #6186 neutron-api-next for 1chb1n mp269302
    AMULET OK: passed

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2015-04-16 21:32:03 +0000
3+++ Makefile 2015-09-02 02:03:35 +0000
4@@ -2,12 +2,18 @@
5 PYTHON := /usr/bin/env python
6
7 lint:
8- @flake8 --exclude hooks/charmhelpers actions hooks unit_tests tests
9+ @flake8 --exclude hooks/charmhelpers,tests/charmhelpers \
10+ actions hooks unit_tests tests
11 @charm proof
12
13-unit_test:
14- @echo Starting tests...
15- @$(PYTHON) /usr/bin/nosetests --nologcapture unit_tests
16+test:
17+ @# Bundletester expects unit tests here.
18+ @echo Starting unit tests...
19+ @$(PYTHON) /usr/bin/nosetests --nologcapture --with-coverage unit_tests
20+
21+functional_test:
22+ @echo Starting Amulet tests...
23+ @juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700
24
25 bin/charm_helpers_sync.py:
26 @mkdir -p bin
27@@ -18,13 +24,6 @@
28 @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-hooks.yaml
29 @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-tests.yaml
30
31-test:
32- @echo Starting Amulet tests...
33- # coreycb note: The -v should only be temporary until Amulet sends
34- # raise_status() messages to stderr:
35- # https://bugs.launchpad.net/amulet/+bug/1320357
36- @juju test -v -p AMULET_HTTP_PROXY,AMULET_OS_VIP --timeout 2700
37-
38-publish: lint unit_test
39+publish: lint test
40 bzr push lp:charms/neutron-api
41 bzr push lp:charms/trusty/neutron-api
42
43=== modified file 'tests/00-setup'
44--- tests/00-setup 2015-07-10 13:34:03 +0000
45+++ tests/00-setup 2015-09-02 02:03:35 +0000
46@@ -4,9 +4,13 @@
47
48 sudo add-apt-repository --yes ppa:juju/stable
49 sudo apt-get update --yes
50-sudo apt-get install --yes python-amulet \
51+sudo apt-get install --yes amulet \
52+ python-cinderclient \
53 python-distro-info \
54+ python-glanceclient \
55+ python-heatclient \
56+ python-keystoneclient \
57 python-neutronclient \
58- python-keystoneclient \
59 python-novaclient \
60- python-glanceclient
61+ python-pika \
62+ python-swiftclient
63
64=== modified file 'tests/019-basic-vivid-kilo' (properties changed: -x to +x)
65=== added file 'tests/020-basic-trusty-liberty'
66--- tests/020-basic-trusty-liberty 1970-01-01 00:00:00 +0000
67+++ tests/020-basic-trusty-liberty 2015-09-02 02:03:35 +0000
68@@ -0,0 +1,11 @@
69+#!/usr/bin/python
70+
71+"""Amulet tests on a basic neutron-api deployment on trusty-liberty."""
72+
73+from basic_deployment import NeutronAPIBasicDeployment
74+
75+if __name__ == '__main__':
76+ deployment = NeutronAPIBasicDeployment(series='trusty',
77+ openstack='cloud:trusty-liberty',
78+ source='cloud:trusty-updates/liberty')
79+ deployment.run_tests()
80
81=== added file 'tests/021-basic-wily-liberty'
82--- tests/021-basic-wily-liberty 1970-01-01 00:00:00 +0000
83+++ tests/021-basic-wily-liberty 2015-09-02 02:03:35 +0000
84@@ -0,0 +1,9 @@
85+#!/usr/bin/python
86+
87+"""Amulet tests on a basic neutron-api deployment on wily-liberty."""
88+
89+from basic_deployment import NeutronAPIBasicDeployment
90+
91+if __name__ == '__main__':
92+ deployment = NeutronAPIBasicDeployment(series='wily')
93+ deployment.run_tests()
94
95=== modified file 'tests/README'
96--- tests/README 2015-02-17 07:10:15 +0000
97+++ tests/README 2015-09-02 02:03:35 +0000
98@@ -1,6 +1,39 @@
99 This directory provides Amulet tests that focus on verification of
100 neutron-api deployments.
101
102+test_* methods are called in lexical sort order, although each individual test
103+should be idempotent, and expected to pass regardless of run order.
104+
105+Test name convention to ensure desired test order:
106+ 1xx service and endpoint checks
107+ 2xx relation checks
108+ 3xx config checks
109+ 4xx functional checks
110+ 9xx restarts and other final checks
111+
112+Common relation definitions:
113+ - [ neutron-api, mysql ]
114+ - [ neutron-api, rabbitmq-server ]
115+ - [ neutron-api, nova-cloud-controller ]
116+ - [ neutron-api, neutron-openvswitch ]
117+ - [ neutron-api, keystone ]
118+ - [ neutron-api, neutron-gateway ]
119+
120+Resultant relations of neutron-api service:
121+ relations:
122+ amqp:
123+ - rabbitmq-server
124+ cluster:
125+ - neutron-api
126+ identity-service:
127+ - keystone
128+ neutron-api:
129+ - nova-cloud-controller
130+ neutron-plugin-api:
131+ - neutron-openvswitch
132+ shared-db:
133+ - mysql
134+
135 In order to run tests, you'll need charm-tools installed (in addition to
136 juju, of course):
137 sudo add-apt-repository ppa:juju/stable
138
139=== modified file 'tests/basic_deployment.py'
140--- tests/basic_deployment.py 2015-07-29 17:23:43 +0000
141+++ tests/basic_deployment.py 2015-09-02 02:03:35 +0000
142@@ -1,38 +1,6 @@
143 #!/usr/bin/python
144 """
145 Basic neutron-api functional test.
146-
147-test_* methods are called in sort order.
148-
149-Convention to ensure desired test order:
150- 1xx service and endpoint checks
151- 2xx relation checks
152- 3xx config checks
153- 4xx functional checks
154- 9xx restarts and other final checks
155-
156-Common relation definitions:
157- - [ neutron-api, mysql ]
158- - [ neutron-api, rabbitmq-server ]
159- - [ neutron-api, nova-cloud-controller ]
160- - [ neutron-api, neutron-openvswitch ]
161- - [ neutron-api, keystone ]
162- - [ neutron-api, neutron-gateway ]
163-
164-Resultant relations of neutron-api service:
165- relations:
166- amqp:
167- - rabbitmq-server
168- cluster:
169- - neutron-api
170- identity-service:
171- - keystone
172- neutron-api:
173- - nova-cloud-controller
174- neutron-plugin-api: # not inspected due to
175- - neutron-openvswitch # bug 1421388
176- shared-db:
177- - mysql
178 """
179
180 import amulet
181@@ -46,8 +14,8 @@
182
183 from charmhelpers.contrib.openstack.amulet.utils import (
184 OpenStackAmuletUtils,
185- DEBUG, # flake8: noqa
186- ERROR
187+ DEBUG,
188+ # ERROR
189 )
190
191 # Use DEBUG to turn on debug logging
192@@ -78,7 +46,8 @@
193 """
194 this_service = {'name': 'neutron-api'}
195 other_services = [{'name': 'mysql'},
196- {'name': 'rabbitmq-server'}, {'name': 'keystone'},
197+ {'name': 'rabbitmq-server'},
198+ {'name': 'keystone'},
199 {'name': 'neutron-openvswitch'},
200 {'name': 'nova-cloud-controller'},
201 {'name': 'neutron-gateway'},
202@@ -94,14 +63,21 @@
203 'neutron-api:neutron-api': 'nova-cloud-controller:neutron-api',
204 'neutron-api:neutron-plugin-api': 'neutron-gateway:'
205 'neutron-plugin-api',
206- 'neutron-api:neutron-plugin-api': 'neutron-openvswitch:'
207- 'neutron-plugin-api',
208 'neutron-api:identity-service': 'keystone:identity-service',
209 'keystone:shared-db': 'mysql:shared-db',
210- 'nova-compute:neutron-plugin': 'neutron-openvswitch:neutron-plugin',
211+ 'nova-compute:neutron-plugin': 'neutron-openvswitch:'
212+ 'neutron-plugin',
213 'nova-cloud-controller:shared-db': 'mysql:shared-db',
214 }
215+
216+ # NOTE(beisner): relate this separately due to the resulting
217+ # duplicate dictionary key if included in the relations dict.
218+ relations_more = {
219+ 'neutron-api:neutron-plugin-api': 'neutron-openvswitch:'
220+ 'neutron-plugin-api',
221+ }
222 super(NeutronAPIBasicDeployment, self)._add_relations(relations)
223+ super(NeutronAPIBasicDeployment, self)._add_relations(relations_more)
224
225 def _configure_services(self):
226 """Configure all of the services."""
227@@ -115,16 +91,16 @@
228 openstack_origin_git = {
229 'repositories': [
230 {'name': 'requirements',
231- 'repository': 'git://github.com/openstack/requirements',
232+ 'repository': 'git://github.com/openstack/requirements', # noqa
233 'branch': branch},
234 {'name': 'neutron-fwaas',
235- 'repository': 'git://github.com/openstack/neutron-fwaas',
236+ 'repository': 'git://github.com/openstack/neutron-fwaas', # noqa
237 'branch': branch},
238 {'name': 'neutron-lbaas',
239- 'repository': 'git://github.com/openstack/neutron-lbaas',
240+ 'repository': 'git://github.com/openstack/neutron-lbaas', # noqa
241 'branch': branch},
242 {'name': 'neutron-vpnaas',
243- 'repository': 'git://github.com/openstack/neutron-vpnaas',
244+ 'repository': 'git://github.com/openstack/neutron-vpnaas', # noqa
245 'branch': branch},
246 {'name': 'neutron',
247 'repository': 'git://github.com/openstack/neutron',
248@@ -154,7 +130,9 @@
249 'http_proxy': amulet_http_proxy,
250 'https_proxy': amulet_http_proxy,
251 }
252- neutron_api_config['openstack-origin-git'] = yaml.dump(openstack_origin_git)
253+ neutron_api_config['openstack-origin-git'] = \
254+ yaml.dump(openstack_origin_git)
255+
256 keystone_config = {'admin-password': 'openstack',
257 'admin-token': 'ubuntutesting'}
258 nova_cc_config = {'network-manager': 'Quantum',
259@@ -171,9 +149,11 @@
260 self.keystone_sentry = self.d.sentry.unit['keystone/0']
261 self.rabbitmq_sentry = self.d.sentry.unit['rabbitmq-server/0']
262 self.nova_cc_sentry = self.d.sentry.unit['nova-cloud-controller/0']
263- self.neutron_gateway_sentry = self.d.sentry.unit['neutron-gateway/0']
264+ self.neutron_gw_sentry = self.d.sentry.unit['neutron-gateway/0']
265 self.neutron_api_sentry = self.d.sentry.unit['neutron-api/0']
266+ self.neutron_ovs_sentry = self.d.sentry.unit['neutron-openvswitch/0']
267 self.nova_compute_sentry = self.d.sentry.unit['nova-compute/0']
268+
269 u.log.debug('openstack release val: {}'.format(
270 self._get_openstack_release()))
271 u.log.debug('openstack release str: {}'.format(
272@@ -181,48 +161,46 @@
273 # Let things settle a bit before moving forward
274 time.sleep(30)
275
276-
277 def test_100_services(self):
278 """Verify the expected services are running on the corresponding
279 service units."""
280 u.log.debug('Checking status of system services...')
281- # Fails vivid-kilo, bug 1454754
282- neutron_api_services = ['status neutron-server']
283- neutron_services = ['status neutron-dhcp-agent',
284- 'status neutron-lbaas-agent',
285- 'status neutron-metadata-agent',
286- 'status neutron-plugin-openvswitch-agent',
287- 'status neutron-ovs-cleanup']
288+ neutron_api_services = ['neutron-server']
289+ neutron_services = ['neutron-dhcp-agent',
290+ 'neutron-lbaas-agent',
291+ 'neutron-metadata-agent',
292+ 'neutron-plugin-openvswitch-agent',
293+ 'neutron-ovs-cleanup']
294
295 if self._get_openstack_release() <= self.trusty_juno:
296- neutron_services.append('status neutron-vpn-agent')
297+ neutron_services.append('neutron-vpn-agent')
298
299 if self._get_openstack_release() < self.trusty_kilo:
300 # Juno or earlier
301- neutron_services.append('status neutron-metering-agent')
302-
303- nova_cc_services = ['status nova-api-ec2',
304- 'status nova-api-os-compute',
305- 'status nova-objectstore',
306- 'status nova-cert',
307- 'status nova-scheduler',
308- 'status nova-conductor']
309-
310- commands = {
311- self.mysql_sentry: ['status mysql'],
312- self.keystone_sentry: ['status keystone'],
313+ neutron_services.append('neutron-metering-agent')
314+
315+ nova_cc_services = ['nova-api-ec2',
316+ 'nova-api-os-compute',
317+ 'nova-objectstore',
318+ 'nova-cert',
319+ 'nova-scheduler',
320+ 'nova-conductor']
321+
322+ services = {
323+ self.mysql_sentry: ['mysql'],
324+ self.keystone_sentry: ['keystone'],
325 self.nova_cc_sentry: nova_cc_services,
326- self.neutron_gateway_sentry: neutron_services,
327+ self.neutron_gw_sentry: neutron_services,
328 self.neutron_api_sentry: neutron_api_services,
329 }
330
331- ret = u.validate_services(commands)
332+ ret = u.validate_services_by_name(services)
333 if ret:
334 amulet.raise_status(amulet.FAIL, msg=ret)
335
336 def test_200_neutron_api_shared_db_relation(self):
337 """Verify the neutron-api to mysql shared-db relation data"""
338- u.log.debug('Checking neutron-api:mysql relation data...')
339+ u.log.debug('Checking neutron-api:mysql db relation data...')
340 unit = self.neutron_api_sentry
341 relation = ['shared-db', 'mysql:shared-db']
342 expected = {
343@@ -239,12 +217,13 @@
344
345 def test_201_shared_db_neutron_api_relation(self):
346 """Verify the mysql to neutron-api shared-db relation data"""
347- u.log.debug('Checking mysql:neutron-api relation data...')
348+ u.log.debug('Checking mysql:neutron-api db relation data...')
349 unit = self.mysql_sentry
350 relation = ['shared-db', 'neutron-api:shared-db']
351 expected = {
352 'db_host': u.valid_ip,
353 'private-address': u.valid_ip,
354+ 'password': u.not_null
355 }
356
357 if self._get_openstack_release() == self.precise_icehouse:
358@@ -255,8 +234,7 @@
359 expected['allowed_units'] = 'neutron-api/0'
360
361 ret = u.validate_relation_data(unit, relation, expected)
362- rel_data = unit.relation('shared-db', 'neutron-api:shared-db')
363- if ret or 'password' not in rel_data:
364+ if ret:
365 message = u.relation_error('mysql shared-db', ret)
366 amulet.raise_status(amulet.FAIL, msg=message)
367
368@@ -281,25 +259,25 @@
369 u.log.debug('Checking amqp:neutron-api relation data...')
370 unit = self.rabbitmq_sentry
371 relation = ['amqp', 'neutron-api:amqp']
372- rel_data = unit.relation('amqp', 'neutron-api:amqp')
373 expected = {
374 'hostname': u.valid_ip,
375 'private-address': u.valid_ip,
376+ 'password': u.not_null
377 }
378
379 ret = u.validate_relation_data(unit, relation, expected)
380- if ret or not 'password' in rel_data:
381+ if ret:
382 message = u.relation_error('rabbitmq amqp', ret)
383 amulet.raise_status(amulet.FAIL, msg=message)
384
385- def test_204_neutron_api_identity_relation(self):
386+ def test_204_neutron_api_keystone_identity_relation(self):
387 """Verify the neutron-api to keystone identity-service relation data"""
388- u.log.debug('Checking neutron-api:keystone relation data...')
389+ u.log.debug('Checking neutron-api:keystone id relation data...')
390 unit = self.neutron_api_sentry
391 relation = ['identity-service', 'keystone:identity-service']
392 api_ip = unit.relation('identity-service',
393 'keystone:identity-service')['private-address']
394- api_endpoint = "http://%s:9696" % (api_ip)
395+ api_endpoint = 'http://{}:9696'.format(api_ip)
396 expected = {
397 'private-address': u.valid_ip,
398 'quantum_region': 'RegionOne',
399@@ -316,12 +294,12 @@
400
401 def test_205_keystone_neutron_api_identity_relation(self):
402 """Verify the keystone to neutron-api identity-service relation data"""
403- u.log.debug('Checking keystone:neutron-api relation data...')
404+ u.log.debug('Checking keystone:neutron-api id relation data...')
405 unit = self.keystone_sentry
406 relation = ['identity-service', 'neutron-api:identity-service']
407- id_relation = unit.relation('identity-service',
408- 'neutron-api:identity-service')
409- id_ip = id_relation['private-address']
410+ rel_ks_id = unit.relation('identity-service',
411+ 'neutron-api:identity-service')
412+ id_ip = rel_ks_id['private-address']
413 expected = {
414 'admin_token': 'ubuntutesting',
415 'auth_host': id_ip,
416@@ -335,12 +313,46 @@
417 message = u.relation_error('neutron-api identity-service', ret)
418 amulet.raise_status(amulet.FAIL, msg=message)
419
420- def test_206_neutron_api_plugin_relation(self):
421+ def test_206_neutron_api_neutron_ovs_plugin_api_relation(self):
422 """Verify neutron-api to neutron-openvswitch neutron-plugin-api"""
423- u.log.debug('Checking neutron-api:neutron-ovs relation data...')
424+ u.log.debug('Checking neutron-api:neutron-ovs plugin-api '
425+ 'relation data...')
426 unit = self.neutron_api_sentry
427 relation = ['neutron-plugin-api',
428 'neutron-openvswitch:neutron-plugin-api']
429+
430+ u.log.debug(unit.relation(relation[0], relation[1]))
431+ expected = {
432+ 'auth_host': u.valid_ip,
433+ 'auth_port': '35357',
434+ 'auth_protocol': 'http',
435+ 'enable-dvr': 'False',
436+ 'enable-l3ha': 'False',
437+ 'l2-population': 'True',
438+ 'neutron-security-groups': 'False',
439+ 'overlay-network-type': 'gre',
440+ 'private-address': u.valid_ip,
441+ 'region': 'RegionOne',
442+ 'service_host': u.valid_ip,
443+ 'service_password': u.not_null,
444+ 'service_port': '5000',
445+ 'service_protocol': 'http',
446+ 'service_tenant': 'services',
447+ 'service_username': 'quantum',
448+ }
449+ ret = u.validate_relation_data(unit, relation, expected)
450+ if ret:
451+ message = u.relation_error(
452+ 'neutron-api neutron-ovs neutronplugin-api', ret)
453+ amulet.raise_status(amulet.FAIL, msg=message)
454+
455+ def test_207_neutron_ovs_neutron_api_plugin_api_relation(self):
456+ """Verify neutron-openvswitch to neutron-api neutron-plugin-api"""
457+ u.log.debug('Checking neutron-ovs:neutron-api plugin-api '
458+ 'relation data...')
459+ unit = self.neutron_ovs_sentry
460+ relation = ['neutron-plugin-api',
461+ 'neutron-api:neutron-plugin-api']
462 expected = {
463 'private-address': u.valid_ip,
464 }
465@@ -349,14 +361,14 @@
466 message = u.relation_error('neutron-api neutron-plugin-api', ret)
467 amulet.raise_status(amulet.FAIL, msg=message)
468
469- def test_207_neutron_api_novacc_relation(self):
470+ def test_208_neutron_api_novacc_relation(self):
471 """Verify the neutron-api to nova-cloud-controller relation data"""
472 u.log.debug('Checking neutron-api:novacc relation data...')
473 unit = self.neutron_api_sentry
474 relation = ['neutron-api', 'nova-cloud-controller:neutron-api']
475 api_ip = unit.relation('identity-service',
476 'keystone:identity-service')['private-address']
477- api_endpoint = "http://%s:9696" % (api_ip)
478+ api_endpoint = 'http://{}:9696'.format(api_ip)
479 expected = {
480 'private-address': api_ip,
481 'neutron-plugin': 'ovs',
482@@ -368,14 +380,14 @@
483 message = u.relation_error('neutron-api neutron-api', ret)
484 amulet.raise_status(amulet.FAIL, msg=message)
485
486- def test_208_novacc_neutron_api_relation(self):
487+ def test_209_novacc_neutron_api_relation(self):
488 """Verify the nova-cloud-controller to neutron-api relation data"""
489 u.log.debug('Checking novacc:neutron-api relation data...')
490 unit = self.nova_cc_sentry
491 relation = ['neutron-api', 'neutron-api:neutron-api']
492 cc_ip = unit.relation('neutron-api',
493 'neutron-api:neutron-api')['private-address']
494- cc_endpoint = "http://%s:8774/v2" % (cc_ip)
495+ cc_endpoint = 'http://{}:8774/v2'.format(cc_ip)
496 expected = {
497 'private-address': cc_ip,
498 'nova_url': cc_endpoint,
499@@ -385,34 +397,6 @@
500 message = u.relation_error('nova-cc neutron-api', ret)
501 amulet.raise_status(amulet.FAIL, msg=message)
502
503- # XXX Test missing to examine the relation data neutron-openvswitch is
504- # receiving. Current;y this data cannot be interegated due to
505- # Bug#1421388
506-
507- def test_900_restart_on_config_change(self):
508- """Verify that the specified services are restarted when the config
509- is changed.
510-
511- Note(coreycb): The method name with the _z_ is a little odd
512- but it forces the test to run last. It just makes things
513- easier because restarting services requires re-authorization.
514- """
515- u.log.debug('Checking novacc neutron-api relation data...')
516- conf = '/etc/neutron/neutron.conf'
517- services = ['neutron-server']
518- u.log.debug('Making config change on neutron-api service...')
519- self.d.configure('neutron-api', {'use-syslog': 'True'})
520- stime = 60
521- for s in services:
522- u.log.debug("Checking that service restarted: {}".format(s))
523- if not u.service_restarted(self.neutron_api_sentry, s, conf,
524- pgrep_full=True, sleep_time=stime):
525- self.d.configure('neutron-api', {'use-syslog': 'False'})
526- msg = "service {} didn't restart after config change".format(s)
527- amulet.raise_status(amulet.FAIL, msg=msg)
528- stime = 0
529- self.d.configure('neutron-api', {'use-syslog': 'False'})
530-
531 def test_300_neutron_config(self):
532 """Verify the data in the neutron config file."""
533 u.log.debug('Checking neutron.conf config file data...')
534@@ -421,16 +405,17 @@
535 'neutron-api:neutron-api')
536 rabbitmq_relation = self.rabbitmq_sentry.relation('amqp',
537 'neutron-api:amqp')
538- ks_rel = self.keystone_sentry.relation('identity-service',
539- 'neutron-api:identity-service')
540+ rel_napi_ks = self.keystone_sentry.relation(
541+ 'identity-service', 'neutron-api:identity-service')
542
543- nova_auth_url = '%s://%s:%s/v2.0' % (ks_rel['auth_protocol'],
544- ks_rel['auth_host'],
545- ks_rel['auth_port'])
546- db_relation = self.mysql_sentry.relation('shared-db',
547+ nova_auth_url = '{}://{}:{}/v2.0'.format(rel_napi_ks['auth_protocol'],
548+ rel_napi_ks['auth_host'],
549+ rel_napi_ks['auth_port'])
550+ rel_napi_db = self.mysql_sentry.relation('shared-db',
551 'neutron-api:shared-db')
552- db_conn = 'mysql://neutron:%s@%s/neutron' % (db_relation['password'],
553- db_relation['db_host'])
554+ db_conn = 'mysql://neutron:{}@{}/neutron'.format(
555+ rel_napi_db['password'], rel_napi_db['db_host'])
556+
557 conf = '/etc/neutron/neutron.conf'
558 expected = {
559 'DEFAULT': {
560@@ -439,16 +424,16 @@
561 'bind_port': '9686',
562 'nova_url': cc_relation['nova_url'],
563 'nova_region_name': 'RegionOne',
564- 'nova_admin_username': ks_rel['service_username'],
565- 'nova_admin_tenant_id': ks_rel['service_tenant_id'],
566- 'nova_admin_password': ks_rel['service_password'],
567+ 'nova_admin_username': rel_napi_ks['service_username'],
568+ 'nova_admin_tenant_id': rel_napi_ks['service_tenant_id'],
569+ 'nova_admin_password': rel_napi_ks['service_password'],
570 'nova_admin_auth_url': nova_auth_url,
571 },
572 'keystone_authtoken': {
573 'signing_dir': '/var/cache/neutron',
574 'admin_tenant_name': 'services',
575 'admin_user': 'quantum',
576- 'admin_password': ks_rel['service_password'],
577+ 'admin_password': rel_napi_ks['service_password'],
578 },
579 'database': {
580 'connection': db_conn,
581@@ -457,36 +442,28 @@
582
583 if self._get_openstack_release() >= self.trusty_kilo:
584 # Kilo or later
585- expected.update(
586- {
587- 'oslo_messaging_rabbit': {
588- 'rabbit_userid': 'neutron',
589- 'rabbit_virtual_host': 'openstack',
590- 'rabbit_password': rabbitmq_relation['password'],
591- 'rabbit_host': rabbitmq_relation['hostname']
592- }
593- }
594- )
595+ expected['oslo_messaging_rabbit'] = {
596+ 'rabbit_userid': 'neutron',
597+ 'rabbit_virtual_host': 'openstack',
598+ 'rabbit_password': rabbitmq_relation['password'],
599+ 'rabbit_host': rabbitmq_relation['hostname']
600+ }
601 else:
602 # Juno or earlier
603- expected['DEFAULT'].update(
604- {
605- 'rabbit_userid': 'neutron',
606- 'rabbit_virtual_host': 'openstack',
607- 'rabbit_password': rabbitmq_relation['password'],
608- 'rabbit_host': rabbitmq_relation['hostname']
609- }
610- )
611- expected['keystone_authtoken'].update(
612- {
613- 'service_protocol': ks_rel['service_protocol'],
614- 'service_host': ks_rel['service_host'],
615- 'service_port': ks_rel['service_port'],
616- 'auth_host': ks_rel['auth_host'],
617- 'auth_port': ks_rel['auth_port'],
618- 'auth_protocol': ks_rel['auth_protocol']
619- }
620- )
621+ expected['DEFAULT'].update({
622+ 'rabbit_userid': 'neutron',
623+ 'rabbit_virtual_host': 'openstack',
624+ 'rabbit_password': rabbitmq_relation['password'],
625+ 'rabbit_host': rabbitmq_relation['hostname']
626+ })
627+ expected['keystone_authtoken'].update({
628+ 'service_protocol': rel_napi_ks['service_protocol'],
629+ 'service_host': rel_napi_ks['service_host'],
630+ 'service_port': rel_napi_ks['service_port'],
631+ 'auth_host': rel_napi_ks['auth_host'],
632+ 'auth_port': rel_napi_ks['auth_port'],
633+ 'auth_protocol': rel_napi_ks['auth_protocol']
634+ })
635
636 for section, pairs in expected.iteritems():
637 ret = u.validate_config_data(unit, conf, section, pairs)
638@@ -527,21 +504,51 @@
639
640 if self._get_openstack_release() >= self.trusty_kilo:
641 # Kilo or later
642- expected['ml2'].update(
643- {
644- 'mechanism_drivers': 'openvswitch,l2population'
645- }
646- )
647+ expected['ml2'].update({
648+ 'mechanism_drivers': 'openvswitch,l2population'
649+ })
650 else:
651 # Juno or earlier
652- expected['ml2'].update(
653- {
654- 'mechanism_drivers': 'openvswitch,hyperv,l2population'
655- }
656- )
657+ expected['ml2'].update({
658+ 'mechanism_drivers': 'openvswitch,hyperv,l2population'
659+ })
660
661 for section, pairs in expected.iteritems():
662 ret = u.validate_config_data(unit, conf, section, pairs)
663 if ret:
664 message = "ml2 config error: {}".format(ret)
665 amulet.raise_status(amulet.FAIL, msg=message)
666+
667+ def test_900_restart_on_config_change(self):
668+ """Verify that the specified services are restarted when the
669+ config is changed."""
670+
671+ sentry = self.neutron_api_sentry
672+ juju_service = 'neutron-api'
673+
674+ # Expected default and alternate values
675+ set_default = {'debug': 'False'}
676+ set_alternate = {'debug': 'True'}
677+
678+ # Services which are expected to restart upon config change,
679+ # and corresponding config files affected by the change
680+ services = {'neutron-server': '/etc/neutron/neutron.conf'}
681+
682+ # Make config change, check for service restarts
683+ u.log.debug('Making config change on {}...'.format(juju_service))
684+ mtime = u.get_sentry_time(sentry)
685+ self.d.configure(juju_service, set_alternate)
686+
687+ for s, conf_file in services.iteritems():
688+ u.log.debug("Checking that service restarted: {}".format(s))
689+ if not u.validate_service_config_changed(sentry, mtime, s,
690+ conf_file,
691+ retry_count=4,
692+ retry_sleep_time=20,
693+ sleep_time=20):
694+ self.d.configure(juju_service, set_default)
695+ msg = "service {} didn't restart after config change".format(s)
696+ amulet.raise_status(amulet.FAIL, msg=msg)
697+
698+ self.d.configure(juju_service, set_default)
699+ u.log.debug('OK')
700
701=== modified file 'tests/charmhelpers/contrib/amulet/utils.py'
702--- tests/charmhelpers/contrib/amulet/utils.py 2015-08-18 17:34:35 +0000
703+++ tests/charmhelpers/contrib/amulet/utils.py 2015-09-02 02:03:35 +0000
704@@ -114,7 +114,7 @@
705 # /!\ DEPRECATION WARNING (beisner):
706 # New and existing tests should be rewritten to use
707 # validate_services_by_name() as it is aware of init systems.
708- self.log.warn('/!\\ DEPRECATION WARNING: use '
709+ self.log.warn('DEPRECATION WARNING: use '
710 'validate_services_by_name instead of validate_services '
711 'due to init system differences.')
712
713@@ -269,33 +269,52 @@
714 """Get last modification time of directory."""
715 return sentry_unit.directory_stat(directory)['mtime']
716
717- def _get_proc_start_time(self, sentry_unit, service, pgrep_full=False):
718- """Get process' start time.
719-
720- Determine start time of the process based on the last modification
721- time of the /proc/pid directory. If pgrep_full is True, the process
722- name is matched against the full command line.
723- """
724- if pgrep_full:
725- cmd = 'pgrep -o -f {}'.format(service)
726- else:
727- cmd = 'pgrep -o {}'.format(service)
728- cmd = cmd + ' | grep -v pgrep || exit 0'
729- cmd_out = sentry_unit.run(cmd)
730- self.log.debug('CMDout: ' + str(cmd_out))
731- if cmd_out[0]:
732- self.log.debug('Pid for %s %s' % (service, str(cmd_out[0])))
733- proc_dir = '/proc/{}'.format(cmd_out[0].strip())
734- return self._get_dir_mtime(sentry_unit, proc_dir)
735+ def _get_proc_start_time(self, sentry_unit, service, pgrep_full=None):
736+ """Get start time of a process based on the last modification time
737+ of the /proc/pid directory.
738+
739+ :sentry_unit: The sentry unit to check for the service on
740+ :service: service name to look for in process table
741+ :pgrep_full: [Deprecated] Use full command line search mode with pgrep
742+ :returns: epoch time of service process start
743+ :param commands: list of bash commands
744+ :param sentry_units: list of sentry unit pointers
745+ :returns: None if successful; Failure message otherwise
746+ """
747+ if pgrep_full is not None:
748+ # /!\ DEPRECATION WARNING (beisner):
749+ # No longer implemented, as pidof is now used instead of pgrep.
750+ # https://bugs.launchpad.net/charm-helpers/+bug/1474030
751+ self.log.warn('DEPRECATION WARNING: pgrep_full bool is no '
752+ 'longer implemented re: lp 1474030.')
753+
754+ pid_list = self.get_process_id_list(sentry_unit, service)
755+ pid = pid_list[0]
756+ proc_dir = '/proc/{}'.format(pid)
757+ self.log.debug('Pid for {} on {}: {}'.format(
758+ service, sentry_unit.info['unit_name'], pid))
759+
760+ return self._get_dir_mtime(sentry_unit, proc_dir)
761
762 def service_restarted(self, sentry_unit, service, filename,
763- pgrep_full=False, sleep_time=20):
764+ pgrep_full=None, sleep_time=20):
765 """Check if service was restarted.
766
767 Compare a service's start time vs a file's last modification time
768 (such as a config file for that service) to determine if the service
769 has been restarted.
770 """
771+ # /!\ DEPRECATION WARNING (beisner):
772+ # This method is prone to races in that no before-time is known.
773+ # Use validate_service_config_changed instead.
774+
775+ # NOTE(beisner) pgrep_full is no longer implemented, as pidof is now
776+ # used instead of pgrep. pgrep_full is still passed through to ensure
777+ # deprecation WARNS. lp1474030
778+ self.log.warn('DEPRECATION WARNING: use '
779+ 'validate_service_config_changed instead of '
780+ 'service_restarted due to known races.')
781+
782 time.sleep(sleep_time)
783 if (self._get_proc_start_time(sentry_unit, service, pgrep_full) >=
784 self._get_file_mtime(sentry_unit, filename)):
785@@ -304,15 +323,15 @@
786 return False
787
788 def service_restarted_since(self, sentry_unit, mtime, service,
789- pgrep_full=False, sleep_time=20,
790- retry_count=2):
791+ pgrep_full=None, sleep_time=20,
792+ retry_count=2, retry_sleep_time=30):
793 """Check if service was been started after a given time.
794
795 Args:
796 sentry_unit (sentry): The sentry unit to check for the service on
797 mtime (float): The epoch time to check against
798 service (string): service name to look for in process table
799- pgrep_full (boolean): Use full command line search mode with pgrep
800+ pgrep_full: [Deprecated] Use full command line search mode with pgrep
801 sleep_time (int): Seconds to sleep before looking for process
802 retry_count (int): If service is not found, how many times to retry
803
804@@ -321,30 +340,44 @@
805 False if service is older than mtime or if service was
806 not found.
807 """
808- self.log.debug('Checking %s restarted since %s' % (service, mtime))
809+ # NOTE(beisner) pgrep_full is no longer implemented, as pidof is now
810+ # used instead of pgrep. pgrep_full is still passed through to ensure
811+ # deprecation WARNS. lp1474030
812+
813+ unit_name = sentry_unit.info['unit_name']
814+ self.log.debug('Checking that %s service restarted since %s on '
815+ '%s' % (service, mtime, unit_name))
816 time.sleep(sleep_time)
817- proc_start_time = self._get_proc_start_time(sentry_unit, service,
818- pgrep_full)
819- while retry_count > 0 and not proc_start_time:
820- self.log.debug('No pid file found for service %s, will retry %i '
821- 'more times' % (service, retry_count))
822- time.sleep(30)
823- proc_start_time = self._get_proc_start_time(sentry_unit, service,
824- pgrep_full)
825- retry_count = retry_count - 1
826+ proc_start_time = None
827+ tries = 0
828+ while tries <= retry_count and not proc_start_time:
829+ try:
830+ proc_start_time = self._get_proc_start_time(sentry_unit,
831+ service,
832+ pgrep_full)
833+ self.log.debug('Attempt {} to get {} proc start time on {} '
834+ 'OK'.format(tries, service, unit_name))
835+ except IOError:
836+ # NOTE(beisner) - race avoidance, proc may not exist yet.
837+ # https://bugs.launchpad.net/charm-helpers/+bug/1474030
838+ self.log.debug('Attempt {} to get {} proc start time on {} '
839+ 'failed'.format(tries, service, unit_name))
840+ time.sleep(retry_sleep_time)
841+ tries += 1
842
843 if not proc_start_time:
844 self.log.warn('No proc start time found, assuming service did '
845 'not start')
846 return False
847 if proc_start_time >= mtime:
848- self.log.debug('proc start time is newer than provided mtime'
849- '(%s >= %s)' % (proc_start_time, mtime))
850+ self.log.debug('Proc start time is newer than provided mtime'
851+ '(%s >= %s) on %s (OK)' % (proc_start_time,
852+ mtime, unit_name))
853 return True
854 else:
855- self.log.warn('proc start time (%s) is older than provided mtime '
856- '(%s), service did not restart' % (proc_start_time,
857- mtime))
858+ self.log.warn('Proc start time (%s) is older than provided mtime '
859+ '(%s) on %s, service did not '
860+ 'restart' % (proc_start_time, mtime, unit_name))
861 return False
862
863 def config_updated_since(self, sentry_unit, filename, mtime,
864@@ -361,12 +394,15 @@
865 bool: True if file was modified more recently than mtime, False if
866 file was modified before mtime,
867 """
868- self.log.debug('Checking %s updated since %s' % (filename, mtime))
869+ self.log.debug('Checking that %s file updated since '
870+ '%s' % (filename, mtime))
871+ unit_name = sentry_unit.info['unit_name']
872 time.sleep(sleep_time)
873 file_mtime = self._get_file_mtime(sentry_unit, filename)
874 if file_mtime >= mtime:
875 self.log.debug('File mtime is newer than provided mtime '
876- '(%s >= %s)' % (file_mtime, mtime))
877+ '(%s >= %s) on %s (OK)' % (file_mtime, mtime,
878+ unit_name))
879 return True
880 else:
881 self.log.warn('File mtime %s is older than provided mtime %s'
882@@ -374,8 +410,9 @@
883 return False
884
885 def validate_service_config_changed(self, sentry_unit, mtime, service,
886- filename, pgrep_full=False,
887- sleep_time=20, retry_count=2):
888+ filename, pgrep_full=None,
889+ sleep_time=20, retry_count=2,
890+ retry_sleep_time=30):
891 """Check service and file were updated after mtime
892
893 Args:
894@@ -383,9 +420,10 @@
895 mtime (float): The epoch time to check against
896 service (string): service name to look for in process table
897 filename (string): The file to check mtime of
898- pgrep_full (boolean): Use full command line search mode with pgrep
899- sleep_time (int): Seconds to sleep before looking for process
900+ pgrep_full: [Deprecated] Use full command line search mode with pgrep
901+ sleep_time (int): Initial sleep in seconds to pass to test helpers
902 retry_count (int): If service is not found, how many times to retry
903+ retry_sleep_time (int): Time in seconds to wait between retries
904
905 Typical Usage:
906 u = OpenStackAmuletUtils(ERROR)
907@@ -402,15 +440,25 @@
908 mtime, False if service is older than mtime or if service was
909 not found or if filename was modified before mtime.
910 """
911- self.log.debug('Checking %s restarted since %s' % (service, mtime))
912- time.sleep(sleep_time)
913- service_restart = self.service_restarted_since(sentry_unit, mtime,
914- service,
915- pgrep_full=pgrep_full,
916- sleep_time=0,
917- retry_count=retry_count)
918- config_update = self.config_updated_since(sentry_unit, filename, mtime,
919- sleep_time=0)
920+
921+ # NOTE(beisner) pgrep_full is no longer implemented, as pidof is now
922+ # used instead of pgrep. pgrep_full is still passed through to ensure
923+ # deprecation WARNS. lp1474030
924+
925+ service_restart = self.service_restarted_since(
926+ sentry_unit, mtime,
927+ service,
928+ pgrep_full=pgrep_full,
929+ sleep_time=sleep_time,
930+ retry_count=retry_count,
931+ retry_sleep_time=retry_sleep_time)
932+
933+ config_update = self.config_updated_since(
934+ sentry_unit,
935+ filename,
936+ mtime,
937+ sleep_time=0)
938+
939 return service_restart and config_update
940
941 def get_sentry_time(self, sentry_unit):
942
943=== added file 'tests/tests.yaml'
944--- tests/tests.yaml 1970-01-01 00:00:00 +0000
945+++ tests/tests.yaml 2015-09-02 02:03:35 +0000
946@@ -0,0 +1,19 @@
947+bootstrap: true
948+reset: true
949+virtualenv: true
950+makefile:
951+ - lint
952+ - test
953+sources:
954+ - ppa:juju/stable
955+packages:
956+ - amulet
957+ - python-cinderclient
958+ - python-distro-info
959+ - python-glanceclient
960+ - python-heatclient
961+ - python-keystoneclient
962+ - python-neutronclient
963+ - python-novaclient
964+ - python-pika
965+ - python-swiftclient

Subscribers

People subscribed via source and target branches