Merge lp:~1chb1n/charms/trusty/openstack-dashboard/next-amulet-fixup-1507 into lp:~openstack-charmers-archive/charms/trusty/openstack-dashboard/next
- Trusty Tahr (14.04)
- next-amulet-fixup-1507
- Merge into next
Status: | Merged |
---|---|
Merged at revision: | 84 |
Proposed branch: | lp:~1chb1n/charms/trusty/openstack-dashboard/next-amulet-fixup-1507 |
Merge into: | lp:~openstack-charmers-archive/charms/trusty/openstack-dashboard/next |
Diff against target: |
699 lines (+287/-180) 7 files modified
Makefile (+10/-10) metadata.yaml (+3/-1) tests/00-setup (+7/-3) tests/README (+10/-0) tests/basic_deployment.py (+136/-113) tests/charmhelpers/contrib/amulet/utils.py (+101/-53) tests/tests.yaml (+20/-0) |
To merge this branch: | bzr merge lp:~1chb1n/charms/trusty/openstack-dashboard/next-amulet-fixup-1507 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Billy Olsen | Pending | ||
OpenStack Charmers | Pending | ||
Review via email: mp+268930@code.launchpad.net |
This proposal supersedes a proposal from 2015-08-01.
Commit message
Description of the change
Update tests for T-K, V-K, prep for T-L and W-L. Clean up old lint. Resolve bug 1474030 race in svc restart checks.
This merge proposal is dependent on the corresponding charm-helpers changes landing:
https:/
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_unit_test #6868 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_amulet_test #5552 openstack-
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://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_amulet_test #5554 openstack-
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://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_lint_check #7428 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_unit_test #6892 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_amulet_test #5557 openstack-
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://
Build: http://
Ryan Beisner (1chb1n) wrote : Posted in a previous version of this proposal | # |
^ NOTE: All non-git amulet tests are passing.
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_lint_check #8490 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_unit_test #7881 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_amulet_test #5951 openstack-
AMULET OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_lint_check #8542 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_unit_test #7931 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_amulet_test #5954 openstack-
AMULET OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_lint_check #8545 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_unit_test #7933 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_amulet_test #5960 openstack-
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://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : Posted in a previous version of this proposal | # |
charm_amulet_test #5959 openstack-
AMULET OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #8651 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #7988 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6016 openstack-
AMULET OK: passed
Build: http://
- 87. By Ryan Beisner
-
update test & resync tests/charmhelpers re: bug 1474030 in service restarted check
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #8705 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #8040 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6031 openstack-
AMULET OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #8830 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #8158 openstack-
UNIT OK: passed
- 88. By Ryan Beisner
-
update test & resync tests/charmhelpers re: bug 1474030 in service restarted check
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #8832 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #8160 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6064 openstack-
AMULET OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6066 openstack-
AMULET OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6072 openstack-
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://
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6073 openstack-
AMULET OK: passed
Build: http://
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #8931 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #8255 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6076 openstack-
AMULET OK: passed
Build: http://
- 89. By Ryan Beisner
-
resync tests/charmhelpers
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_lint_check #9225 openstack-
LINT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_unit_test #8526 openstack-
UNIT OK: passed
uosci-testing-bot (uosci-testing-bot) wrote : | # |
charm_amulet_test #6188 openstack-
AMULET OK: passed
Build: http://
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2015-04-16 21:32:06 +0000 |
3 | +++ Makefile 2015-09-02 02:02:17 +0000 |
4 | @@ -2,12 +2,19 @@ |
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 | +test: |
15 | + @# Bundletester expects unit tests here. |
16 | @echo Starting tests... |
17 | - @$(PYTHON) /usr/bin/nosetests --nologcapture unit_tests |
18 | + @$(PYTHON) /usr/bin/nosetests --nologcapture --with-coverage unit_tests |
19 | + |
20 | +functional_test: |
21 | + @echo Starting Amulet tests... |
22 | + # https://bugs.launchpad.net/amulet/+bug/1320357 |
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 +25,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 test |
39 | bzr push lp:charms/openstack-dashboard |
40 | bzr push lp:charms/trusty/openstack-dashboard |
41 | |
42 | === modified file 'metadata.yaml' |
43 | --- metadata.yaml 2014-10-30 03:30:36 +0000 |
44 | +++ metadata.yaml 2015-09-02 02:02:17 +0000 |
45 | @@ -4,7 +4,9 @@ |
46 | description: | |
47 | The OpenStack Dashboard provides a full feature web interface for interacting |
48 | with instances, images, volumes and networks within an OpenStack deployment. |
49 | -categories: ["misc"] |
50 | +tags: |
51 | + - openstack |
52 | + - misc |
53 | provides: |
54 | nrpe-external-master: |
55 | interface: nrpe-external-master |
56 | |
57 | === modified file 'tests/00-setup' |
58 | --- tests/00-setup 2015-07-10 13:34:04 +0000 |
59 | +++ tests/00-setup 2015-09-02 02:02:17 +0000 |
60 | @@ -4,9 +4,13 @@ |
61 | |
62 | sudo add-apt-repository --yes ppa:juju/stable |
63 | sudo apt-get update --yes |
64 | -sudo apt-get install --yes python-amulet \ |
65 | +sudo apt-get install --yes amulet \ |
66 | + python-cinderclient \ |
67 | python-distro-info \ |
68 | + python-glanceclient \ |
69 | + python-heatclient \ |
70 | + python-keystoneclient \ |
71 | python-neutronclient \ |
72 | - python-keystoneclient \ |
73 | python-novaclient \ |
74 | - python-glanceclient |
75 | + python-pika \ |
76 | + python-swiftclient |
77 | |
78 | === modified file 'tests/019-basic-vivid-kilo' (properties changed: -x to +x) |
79 | === modified file 'tests/README' |
80 | --- tests/README 2015-02-12 13:03:59 +0000 |
81 | +++ tests/README 2015-09-02 02:02:17 +0000 |
82 | @@ -1,6 +1,16 @@ |
83 | This directory provides Amulet tests that focus on verification of |
84 | openstack-dashboard deployments. |
85 | |
86 | +test_* methods are called in lexical sort order, although each individual test |
87 | +should be idempotent, and expected to pass regardless of run order. |
88 | + |
89 | +Test name convention to ensure desired test order: |
90 | + 1xx service and endpoint checks |
91 | + 2xx relation checks |
92 | + 3xx config checks |
93 | + 4xx functional checks |
94 | + 9xx restarts and other final checks |
95 | + |
96 | In order to run tests, you'll need charm-tools installed (in addition to |
97 | juju, of course): |
98 | sudo add-apt-repository ppa:juju/stable |
99 | |
100 | === modified file 'tests/basic_deployment.py' |
101 | --- tests/basic_deployment.py 2015-08-05 01:42:01 +0000 |
102 | +++ tests/basic_deployment.py 2015-09-02 02:02:17 +0000 |
103 | @@ -12,8 +12,8 @@ |
104 | |
105 | from charmhelpers.contrib.openstack.amulet.utils import ( |
106 | OpenStackAmuletUtils, |
107 | - DEBUG, # flake8: noqa |
108 | - ERROR |
109 | + DEBUG, |
110 | + # ERROR |
111 | ) |
112 | |
113 | # Use DEBUG to turn on debug logging |
114 | @@ -26,8 +26,10 @@ |
115 | def __init__(self, series, openstack=None, source=None, git=False, |
116 | stable=False): |
117 | """Deploy the entire test environment.""" |
118 | - super(OpenstackDashboardBasicDeployment, self).__init__(series, openstack, |
119 | - source, stable) |
120 | + super(OpenstackDashboardBasicDeployment, self).__init__(series, |
121 | + openstack, |
122 | + source, |
123 | + stable) |
124 | self.git = git |
125 | self._add_services() |
126 | self._add_relations() |
127 | @@ -36,24 +38,26 @@ |
128 | self._initialize_tests() |
129 | |
130 | def _add_services(self): |
131 | - """Add services |
132 | - |
133 | - Add the services that we're testing, where openstack-dashboard is local, |
134 | - and the rest of the service are from lp branches that are |
135 | - compatible with the local charm (e.g. stable or next). |
136 | - """ |
137 | + """Add the services that we're testing, where openstack-dashboard is |
138 | + local, and the rest of the service are from lp branches that are |
139 | + compatible with the local charm (e.g. stable or next). |
140 | + """ |
141 | this_service = {'name': 'openstack-dashboard'} |
142 | - other_services = [{'name': 'keystone'}, {'name': 'mysql'}] |
143 | - super(OpenstackDashboardBasicDeployment, self)._add_services(this_service, |
144 | - other_services) |
145 | + other_services = [{'name': 'keystone'}, |
146 | + {'name': 'mysql'}] |
147 | + super(OpenstackDashboardBasicDeployment, self)._add_services( |
148 | + this_service, |
149 | + other_services) |
150 | |
151 | def _add_relations(self): |
152 | """Add all of the relations for the services.""" |
153 | relations = { |
154 | - 'openstack-dashboard:identity-service': 'keystone:identity-service', |
155 | - 'keystone:shared-db': 'mysql:shared-db', |
156 | + 'openstack-dashboard:identity-service': |
157 | + 'keystone:identity-service', |
158 | + 'keystone:shared-db': 'mysql:shared-db', |
159 | } |
160 | - super(OpenstackDashboardBasicDeployment, self)._add_relations(relations) |
161 | + super(OpenstackDashboardBasicDeployment, self)._add_relations( |
162 | + relations) |
163 | |
164 | def _configure_services(self): |
165 | """Configure all of the services.""" |
166 | @@ -75,16 +79,17 @@ |
167 | {'name': 'requirements', |
168 | 'repository': reqs_repo, |
169 | 'branch': branch}, |
170 | - # NOTE(coreycb): Pin oslo libraries here because they're not |
171 | - # capped and recently released versions causing issues for juno. |
172 | + # NOTE(coreycb): Pin oslo libraries here because |
173 | + # they're not capped and recently released versions |
174 | + # causing issues for juno. |
175 | {'name': 'oslo-config', |
176 | - 'repository': 'git://github.com/openstack/oslo.config', |
177 | + 'repository': 'git://github.com/openstack/oslo.config', # noqa |
178 | 'branch': '1.6.0'}, |
179 | {'name': 'oslo-i18n', |
180 | 'repository': 'git://github.com/openstack/oslo.i18n', |
181 | 'branch': '1.3.1'}, |
182 | {'name': 'oslo-serialization', |
183 | - 'repository': 'git://github.com/openstack/oslo.serialization', |
184 | + 'repository': 'git://github.com/openstack/oslo.serialization', # noqa |
185 | 'branch': '1.2.0'}, |
186 | {'name': 'oslo-utils', |
187 | 'repository': 'git://github.com/openstack/oslo.utils', |
188 | @@ -111,7 +116,8 @@ |
189 | 'http_proxy': amulet_http_proxy, |
190 | 'https_proxy': amulet_http_proxy, |
191 | } |
192 | - horizon_config['openstack-origin-git'] = yaml.dump(openstack_origin_git) |
193 | + horizon_config['openstack-origin-git'] = \ |
194 | + yaml.dump(openstack_origin_git) |
195 | |
196 | keystone_config = {'admin-password': 'openstack', |
197 | 'admin-token': 'ubuntutesting'} |
198 | @@ -119,57 +125,91 @@ |
199 | configs = {'openstack-dashboard': horizon_config, |
200 | 'mysql': mysql_config, |
201 | 'keystone': keystone_config} |
202 | - super(OpenstackDashboardBasicDeployment, self)._configure_services(configs) |
203 | + super(OpenstackDashboardBasicDeployment, self)._configure_services( |
204 | + configs) |
205 | |
206 | def _initialize_tests(self): |
207 | """Perform final initialization before tests get run.""" |
208 | # Access the sentries for inspecting service units |
209 | self.keystone_sentry = self.d.sentry.unit['keystone/0'] |
210 | - self.openstack_dashboard_sentry = self.d.sentry.unit['openstack-dashboard/0'] |
211 | - |
212 | - def test_services(self): |
213 | + self.openstack_dashboard_sentry = \ |
214 | + self.d.sentry.unit['openstack-dashboard/0'] |
215 | + |
216 | + u.log.debug('openstack release val: {}'.format( |
217 | + self._get_openstack_release())) |
218 | + u.log.debug('openstack release str: {}'.format( |
219 | + self._get_openstack_release_string())) |
220 | + # Let things settle a bit before moving forward |
221 | + time.sleep(30) |
222 | + |
223 | + # NOTE(beisner): Switch to helper once the rabbitmq test refactor lands. |
224 | + def crude_py_parse(self, file_contents, expected): |
225 | + for line in file_contents.split('\n'): |
226 | + if '=' in line: |
227 | + args = line.split('=') |
228 | + if len(args) <= 1: |
229 | + continue |
230 | + key = args[0].strip() |
231 | + value = args[1].strip() |
232 | + if key in expected.keys(): |
233 | + if expected[key] != value: |
234 | + msg = "Mismatch %s != %s" % (expected[key], value) |
235 | + amulet.raise_status(amulet.FAIL, msg=msg) |
236 | + |
237 | + def test_100_services(self): |
238 | """Verify the expected services are running on the corresponding |
239 | service units.""" |
240 | - dashboard_services = ['service apache2 status'] |
241 | - |
242 | - commands = { |
243 | - self.keystone_sentry: ['status keystone'], |
244 | - self.openstack_dashboard_sentry: dashboard_services |
245 | + services = { |
246 | + self.keystone_sentry: ['keystone'], |
247 | + self.openstack_dashboard_sentry: ['apache2'] |
248 | } |
249 | |
250 | - ret = u.validate_services(commands) |
251 | + ret = u.validate_services_by_name(services) |
252 | if ret: |
253 | amulet.raise_status(amulet.FAIL, msg=ret) |
254 | |
255 | - def crude_py_parse(self, file_contents, expected): |
256 | - for line in file_contents.split('\n'): |
257 | - if '=' in line: |
258 | - args = line.split('=') |
259 | - if len(args) <= 1: |
260 | - continue |
261 | - key = args[0].strip() |
262 | - value = args[1].strip() |
263 | - if key in expected.keys(): |
264 | - if expected[key] != value: |
265 | - msg="Mismatch %s != %s" % (expected[key], value) |
266 | - amulet.raise_status(amulet.FAIL, msg=msg) |
267 | - |
268 | - |
269 | - def test_local_settings(self): |
270 | + def test_200_openstack_dashboard_identity_service_relation(self): |
271 | + """Verify the openstack-dashboard to keystone identity-service |
272 | + relation data.""" |
273 | + u.log.debug('Checking dashboard:keystone id relation data...') |
274 | unit = self.openstack_dashboard_sentry |
275 | - ksentry = self.keystone_sentry |
276 | - conf = '/etc/openstack-dashboard/local_settings.py' |
277 | - file_contents = unit.file_contents(conf) |
278 | - rdata = ksentry.relation('identity-service', 'openstack-dashboard:identity-service') |
279 | - expected = { |
280 | - 'LOGIN_REDIRECT_URL': """'/horizon'""", |
281 | - 'OPENSTACK_HOST': '"%s"' % (rdata['private-address']), |
282 | - 'OPENSTACK_KEYSTONE_DEFAULT_ROLE': '"Member"' |
283 | - } |
284 | - self.crude_py_parse(file_contents, expected) |
285 | - |
286 | - def test_router_settings(self): |
287 | - if self.openstack > "icehouse": |
288 | + relation = ['identity-service', 'keystone:identity-service'] |
289 | + expected = { |
290 | + 'private-address': u.valid_ip, |
291 | + 'requested_roles': 'Member', |
292 | + } |
293 | + |
294 | + ret = u.validate_relation_data(unit, relation, expected) |
295 | + if ret: |
296 | + message = u.relation_error('openstack-dashboard identity-service', |
297 | + ret) |
298 | + amulet.raise_status(amulet.FAIL, msg=message) |
299 | + |
300 | + def test_202_keystone_identity_service_relation(self): |
301 | + """Verify the keystone to openstack-dashboard identity-service |
302 | + relation data.""" |
303 | + u.log.debug('Checking keystone:dashboard id relation data...') |
304 | + unit = self.keystone_sentry |
305 | + relation = ['identity-service', 'openstack-dashboard:identity-service'] |
306 | + expected = { |
307 | + 'auth_host': u.valid_ip, |
308 | + 'auth_port': '35357', |
309 | + 'auth_protocol': 'http', |
310 | + 'private-address': u.valid_ip, |
311 | + 'region': 'RegionOne', |
312 | + 'service_host': u.valid_ip, |
313 | + 'service_port': '5000', |
314 | + 'service_protocol': 'http', |
315 | + } |
316 | + |
317 | + ret = u.validate_relation_data(unit, relation, expected) |
318 | + if ret: |
319 | + message = u.relation_error('keystone identity-service', ret) |
320 | + amulet.raise_status(amulet.FAIL, msg=message) |
321 | + |
322 | + def test_302_router_settings(self): |
323 | + if self._get_openstack_release() > self.trusty_icehouse: |
324 | + u.log.debug('Checking dashboard router settings...') |
325 | unit = self.openstack_dashboard_sentry |
326 | conf = ('/usr/share/openstack-dashboard/openstack_dashboard/' |
327 | 'enabled/_40_router.py') |
328 | @@ -179,7 +219,8 @@ |
329 | } |
330 | self.crude_py_parse(file_contents, expected) |
331 | |
332 | - def test_connection(self): |
333 | + def test_400_connection(self): |
334 | + u.log.debug('Checking dashboard http response...') |
335 | unit = self.openstack_dashboard_sentry |
336 | dashboard_relation = unit.relation('identity-service', |
337 | 'keystone:identity-service') |
338 | @@ -187,59 +228,41 @@ |
339 | response = urllib2.urlopen('http://%s/horizon' % (dashboard_ip)) |
340 | html = response.read() |
341 | if 'OpenStack Dashboard' not in html: |
342 | - msg="Dashboard frontpage check failed" |
343 | + msg = "Dashboard frontpage check failed" |
344 | amulet.raise_status(amulet.FAIL, msg=msg) |
345 | |
346 | - def test_z_restart_on_config_change(self): |
347 | - """Verify that the specified services are restarted when the config |
348 | - is changed. |
349 | - |
350 | - Note(coreycb): The method name with the _z_ is a little odd |
351 | - but it forces the test to run last. It just makes things |
352 | - easier because restarting services requires re-authorization. |
353 | - """ |
354 | - conf = '/etc/openstack-dashboard/local_settings.py' |
355 | - services = ['apache2'] |
356 | - self.d.configure('openstack-dashboard', {'use-syslog': 'True'}) |
357 | - time = 120 |
358 | - for s in services: |
359 | - if not u.service_restarted(self.openstack_dashboard_sentry, s, conf, |
360 | - pgrep_full=True, sleep_time=time): |
361 | - self.d.configure('openstack-dashboard', {'use-syslog': 'False'}) |
362 | + def test_900_restart_on_config_change(self): |
363 | + """Verify that the specified services are restarted when the |
364 | + config is changed.""" |
365 | + |
366 | + sentry = self.openstack_dashboard_sentry |
367 | + juju_service = 'openstack-dashboard' |
368 | + |
369 | + # Expected default and alternate values |
370 | + set_default = {'use-syslog': 'False'} |
371 | + set_alternate = {'use-syslog': 'True'} |
372 | + |
373 | + # Services which are expected to restart upon config change, |
374 | + # and corresponding config files affected by the change |
375 | + services = {'apache2': '/etc/openstack-dashboard/local_settings.py'} |
376 | + |
377 | + # Make config change, check for service restarts |
378 | + u.log.debug('Making config change on {}...'.format(juju_service)) |
379 | + mtime = u.get_sentry_time(sentry) |
380 | + self.d.configure(juju_service, set_alternate) |
381 | + |
382 | + sleep_time = 30 |
383 | + for s, conf_file in services.iteritems(): |
384 | + u.log.debug("Checking that service restarted: {}".format(s)) |
385 | + if not u.validate_service_config_changed(sentry, mtime, s, |
386 | + conf_file, |
387 | + retry_count=6, |
388 | + retry_sleep_time=20, |
389 | + sleep_time=sleep_time): |
390 | + |
391 | + self.d.configure(juju_service, set_default) |
392 | msg = "service {} didn't restart after config change".format(s) |
393 | - time = 0 |
394 | - self.d.configure('openstack-dashboard', {'use-syslog': 'False'}) |
395 | - |
396 | - def test_openstack_dashboard_identity_service_relation(self): |
397 | - """Verify the openstack-dashboard to keystone identity-service relation data.""" |
398 | - unit = self.openstack_dashboard_sentry |
399 | - relation = ['identity-service', 'keystone:identity-service'] |
400 | - expected = { |
401 | - 'private-address': u.valid_ip, |
402 | - 'requested_roles': 'Member', |
403 | - } |
404 | - |
405 | - ret = u.validate_relation_data(unit, relation, expected) |
406 | - if ret: |
407 | - message = u.relation_error('openstack-dashboard identity-service', ret) |
408 | - amulet.raise_status(amulet.FAIL, msg=message) |
409 | - |
410 | - def test_keystone_identity_service_relation(self): |
411 | - """Verify the keystone to openstack-dashboard identity-service relation data.""" |
412 | - unit = self.keystone_sentry |
413 | - relation = ['identity-service', 'openstack-dashboard:identity-service'] |
414 | - expected = { |
415 | - 'auth_host': u.valid_ip, |
416 | - 'auth_port': '35357', |
417 | - 'auth_protocol': 'http', |
418 | - 'private-address': u.valid_ip, |
419 | - 'region': 'RegionOne', |
420 | - 'service_host': u.valid_ip, |
421 | - 'service_port': '5000', |
422 | - 'service_protocol': 'http', |
423 | - } |
424 | - |
425 | - ret = u.validate_relation_data(unit, relation, expected) |
426 | - if ret: |
427 | - message = u.relation_error('keystone identity-service', ret) |
428 | - amulet.raise_status(amulet.FAIL, msg=message) |
429 | + amulet.raise_status(amulet.FAIL, msg=msg) |
430 | + sleep_time = 0 |
431 | + |
432 | + self.d.configure(juju_service, set_default) |
433 | |
434 | === modified file 'tests/charmhelpers/contrib/amulet/utils.py' |
435 | --- tests/charmhelpers/contrib/amulet/utils.py 2015-08-18 17:34:36 +0000 |
436 | +++ tests/charmhelpers/contrib/amulet/utils.py 2015-09-02 02:02:17 +0000 |
437 | @@ -114,7 +114,7 @@ |
438 | # /!\ DEPRECATION WARNING (beisner): |
439 | # New and existing tests should be rewritten to use |
440 | # validate_services_by_name() as it is aware of init systems. |
441 | - self.log.warn('/!\\ DEPRECATION WARNING: use ' |
442 | + self.log.warn('DEPRECATION WARNING: use ' |
443 | 'validate_services_by_name instead of validate_services ' |
444 | 'due to init system differences.') |
445 | |
446 | @@ -269,33 +269,52 @@ |
447 | """Get last modification time of directory.""" |
448 | return sentry_unit.directory_stat(directory)['mtime'] |
449 | |
450 | - def _get_proc_start_time(self, sentry_unit, service, pgrep_full=False): |
451 | - """Get process' start time. |
452 | - |
453 | - Determine start time of the process based on the last modification |
454 | - time of the /proc/pid directory. If pgrep_full is True, the process |
455 | - name is matched against the full command line. |
456 | - """ |
457 | - if pgrep_full: |
458 | - cmd = 'pgrep -o -f {}'.format(service) |
459 | - else: |
460 | - cmd = 'pgrep -o {}'.format(service) |
461 | - cmd = cmd + ' | grep -v pgrep || exit 0' |
462 | - cmd_out = sentry_unit.run(cmd) |
463 | - self.log.debug('CMDout: ' + str(cmd_out)) |
464 | - if cmd_out[0]: |
465 | - self.log.debug('Pid for %s %s' % (service, str(cmd_out[0]))) |
466 | - proc_dir = '/proc/{}'.format(cmd_out[0].strip()) |
467 | - return self._get_dir_mtime(sentry_unit, proc_dir) |
468 | + def _get_proc_start_time(self, sentry_unit, service, pgrep_full=None): |
469 | + """Get start time of a process based on the last modification time |
470 | + of the /proc/pid directory. |
471 | + |
472 | + :sentry_unit: The sentry unit to check for the service on |
473 | + :service: service name to look for in process table |
474 | + :pgrep_full: [Deprecated] Use full command line search mode with pgrep |
475 | + :returns: epoch time of service process start |
476 | + :param commands: list of bash commands |
477 | + :param sentry_units: list of sentry unit pointers |
478 | + :returns: None if successful; Failure message otherwise |
479 | + """ |
480 | + if pgrep_full is not None: |
481 | + # /!\ DEPRECATION WARNING (beisner): |
482 | + # No longer implemented, as pidof is now used instead of pgrep. |
483 | + # https://bugs.launchpad.net/charm-helpers/+bug/1474030 |
484 | + self.log.warn('DEPRECATION WARNING: pgrep_full bool is no ' |
485 | + 'longer implemented re: lp 1474030.') |
486 | + |
487 | + pid_list = self.get_process_id_list(sentry_unit, service) |
488 | + pid = pid_list[0] |
489 | + proc_dir = '/proc/{}'.format(pid) |
490 | + self.log.debug('Pid for {} on {}: {}'.format( |
491 | + service, sentry_unit.info['unit_name'], pid)) |
492 | + |
493 | + return self._get_dir_mtime(sentry_unit, proc_dir) |
494 | |
495 | def service_restarted(self, sentry_unit, service, filename, |
496 | - pgrep_full=False, sleep_time=20): |
497 | + pgrep_full=None, sleep_time=20): |
498 | """Check if service was restarted. |
499 | |
500 | Compare a service's start time vs a file's last modification time |
501 | (such as a config file for that service) to determine if the service |
502 | has been restarted. |
503 | """ |
504 | + # /!\ DEPRECATION WARNING (beisner): |
505 | + # This method is prone to races in that no before-time is known. |
506 | + # Use validate_service_config_changed instead. |
507 | + |
508 | + # NOTE(beisner) pgrep_full is no longer implemented, as pidof is now |
509 | + # used instead of pgrep. pgrep_full is still passed through to ensure |
510 | + # deprecation WARNS. lp1474030 |
511 | + self.log.warn('DEPRECATION WARNING: use ' |
512 | + 'validate_service_config_changed instead of ' |
513 | + 'service_restarted due to known races.') |
514 | + |
515 | time.sleep(sleep_time) |
516 | if (self._get_proc_start_time(sentry_unit, service, pgrep_full) >= |
517 | self._get_file_mtime(sentry_unit, filename)): |
518 | @@ -304,15 +323,15 @@ |
519 | return False |
520 | |
521 | def service_restarted_since(self, sentry_unit, mtime, service, |
522 | - pgrep_full=False, sleep_time=20, |
523 | - retry_count=2): |
524 | + pgrep_full=None, sleep_time=20, |
525 | + retry_count=2, retry_sleep_time=30): |
526 | """Check if service was been started after a given time. |
527 | |
528 | Args: |
529 | sentry_unit (sentry): The sentry unit to check for the service on |
530 | mtime (float): The epoch time to check against |
531 | service (string): service name to look for in process table |
532 | - pgrep_full (boolean): Use full command line search mode with pgrep |
533 | + pgrep_full: [Deprecated] Use full command line search mode with pgrep |
534 | sleep_time (int): Seconds to sleep before looking for process |
535 | retry_count (int): If service is not found, how many times to retry |
536 | |
537 | @@ -321,30 +340,44 @@ |
538 | False if service is older than mtime or if service was |
539 | not found. |
540 | """ |
541 | - self.log.debug('Checking %s restarted since %s' % (service, mtime)) |
542 | + # NOTE(beisner) pgrep_full is no longer implemented, as pidof is now |
543 | + # used instead of pgrep. pgrep_full is still passed through to ensure |
544 | + # deprecation WARNS. lp1474030 |
545 | + |
546 | + unit_name = sentry_unit.info['unit_name'] |
547 | + self.log.debug('Checking that %s service restarted since %s on ' |
548 | + '%s' % (service, mtime, unit_name)) |
549 | time.sleep(sleep_time) |
550 | - proc_start_time = self._get_proc_start_time(sentry_unit, service, |
551 | - pgrep_full) |
552 | - while retry_count > 0 and not proc_start_time: |
553 | - self.log.debug('No pid file found for service %s, will retry %i ' |
554 | - 'more times' % (service, retry_count)) |
555 | - time.sleep(30) |
556 | - proc_start_time = self._get_proc_start_time(sentry_unit, service, |
557 | - pgrep_full) |
558 | - retry_count = retry_count - 1 |
559 | + proc_start_time = None |
560 | + tries = 0 |
561 | + while tries <= retry_count and not proc_start_time: |
562 | + try: |
563 | + proc_start_time = self._get_proc_start_time(sentry_unit, |
564 | + service, |
565 | + pgrep_full) |
566 | + self.log.debug('Attempt {} to get {} proc start time on {} ' |
567 | + 'OK'.format(tries, service, unit_name)) |
568 | + except IOError: |
569 | + # NOTE(beisner) - race avoidance, proc may not exist yet. |
570 | + # https://bugs.launchpad.net/charm-helpers/+bug/1474030 |
571 | + self.log.debug('Attempt {} to get {} proc start time on {} ' |
572 | + 'failed'.format(tries, service, unit_name)) |
573 | + time.sleep(retry_sleep_time) |
574 | + tries += 1 |
575 | |
576 | if not proc_start_time: |
577 | self.log.warn('No proc start time found, assuming service did ' |
578 | 'not start') |
579 | return False |
580 | if proc_start_time >= mtime: |
581 | - self.log.debug('proc start time is newer than provided mtime' |
582 | - '(%s >= %s)' % (proc_start_time, mtime)) |
583 | + self.log.debug('Proc start time is newer than provided mtime' |
584 | + '(%s >= %s) on %s (OK)' % (proc_start_time, |
585 | + mtime, unit_name)) |
586 | return True |
587 | else: |
588 | - self.log.warn('proc start time (%s) is older than provided mtime ' |
589 | - '(%s), service did not restart' % (proc_start_time, |
590 | - mtime)) |
591 | + self.log.warn('Proc start time (%s) is older than provided mtime ' |
592 | + '(%s) on %s, service did not ' |
593 | + 'restart' % (proc_start_time, mtime, unit_name)) |
594 | return False |
595 | |
596 | def config_updated_since(self, sentry_unit, filename, mtime, |
597 | @@ -361,12 +394,15 @@ |
598 | bool: True if file was modified more recently than mtime, False if |
599 | file was modified before mtime, |
600 | """ |
601 | - self.log.debug('Checking %s updated since %s' % (filename, mtime)) |
602 | + self.log.debug('Checking that %s file updated since ' |
603 | + '%s' % (filename, mtime)) |
604 | + unit_name = sentry_unit.info['unit_name'] |
605 | time.sleep(sleep_time) |
606 | file_mtime = self._get_file_mtime(sentry_unit, filename) |
607 | if file_mtime >= mtime: |
608 | self.log.debug('File mtime is newer than provided mtime ' |
609 | - '(%s >= %s)' % (file_mtime, mtime)) |
610 | + '(%s >= %s) on %s (OK)' % (file_mtime, mtime, |
611 | + unit_name)) |
612 | return True |
613 | else: |
614 | self.log.warn('File mtime %s is older than provided mtime %s' |
615 | @@ -374,8 +410,9 @@ |
616 | return False |
617 | |
618 | def validate_service_config_changed(self, sentry_unit, mtime, service, |
619 | - filename, pgrep_full=False, |
620 | - sleep_time=20, retry_count=2): |
621 | + filename, pgrep_full=None, |
622 | + sleep_time=20, retry_count=2, |
623 | + retry_sleep_time=30): |
624 | """Check service and file were updated after mtime |
625 | |
626 | Args: |
627 | @@ -383,9 +420,10 @@ |
628 | mtime (float): The epoch time to check against |
629 | service (string): service name to look for in process table |
630 | filename (string): The file to check mtime of |
631 | - pgrep_full (boolean): Use full command line search mode with pgrep |
632 | - sleep_time (int): Seconds to sleep before looking for process |
633 | + pgrep_full: [Deprecated] Use full command line search mode with pgrep |
634 | + sleep_time (int): Initial sleep in seconds to pass to test helpers |
635 | retry_count (int): If service is not found, how many times to retry |
636 | + retry_sleep_time (int): Time in seconds to wait between retries |
637 | |
638 | Typical Usage: |
639 | u = OpenStackAmuletUtils(ERROR) |
640 | @@ -402,15 +440,25 @@ |
641 | mtime, False if service is older than mtime or if service was |
642 | not found or if filename was modified before mtime. |
643 | """ |
644 | - self.log.debug('Checking %s restarted since %s' % (service, mtime)) |
645 | - time.sleep(sleep_time) |
646 | - service_restart = self.service_restarted_since(sentry_unit, mtime, |
647 | - service, |
648 | - pgrep_full=pgrep_full, |
649 | - sleep_time=0, |
650 | - retry_count=retry_count) |
651 | - config_update = self.config_updated_since(sentry_unit, filename, mtime, |
652 | - sleep_time=0) |
653 | + |
654 | + # NOTE(beisner) pgrep_full is no longer implemented, as pidof is now |
655 | + # used instead of pgrep. pgrep_full is still passed through to ensure |
656 | + # deprecation WARNS. lp1474030 |
657 | + |
658 | + service_restart = self.service_restarted_since( |
659 | + sentry_unit, mtime, |
660 | + service, |
661 | + pgrep_full=pgrep_full, |
662 | + sleep_time=sleep_time, |
663 | + retry_count=retry_count, |
664 | + retry_sleep_time=retry_sleep_time) |
665 | + |
666 | + config_update = self.config_updated_since( |
667 | + sentry_unit, |
668 | + filename, |
669 | + mtime, |
670 | + sleep_time=0) |
671 | + |
672 | return service_restart and config_update |
673 | |
674 | def get_sentry_time(self, sentry_unit): |
675 | |
676 | === added file 'tests/tests.yaml' |
677 | --- tests/tests.yaml 1970-01-01 00:00:00 +0000 |
678 | +++ tests/tests.yaml 2015-09-02 02:02:17 +0000 |
679 | @@ -0,0 +1,20 @@ |
680 | +bootstrap: true |
681 | +reset: true |
682 | +virtualenv: true |
683 | +makefile: |
684 | + - lint |
685 | + - test |
686 | +sources: |
687 | + - ppa:juju/stable |
688 | +packages: |
689 | + - amulet |
690 | + - python-amulet |
691 | + - python-cinderclient |
692 | + - python-distro-info |
693 | + - python-glanceclient |
694 | + - python-heatclient |
695 | + - python-keystoneclient |
696 | + - python-neutronclient |
697 | + - python-novaclient |
698 | + - python-pika |
699 | + - python-swiftclient |
charm_lint_check #7402 openstack- dashboard- next for 1chb1n mp266642
LINT OK: passed
Build: http:// 10.245. 162.77: 8080/job/ charm_lint_ check/7402/