Merge lp:~1chb1n/charm-helpers/heat-amulet into lp:charm-helpers
- heat-amulet
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 388 |
Proposed branch: | lp:~1chb1n/charm-helpers/heat-amulet |
Merge into: | lp:charm-helpers |
Diff against target: |
481 lines (+227/-12) 4 files modified
charmhelpers/contrib/amulet/utils.py (+91/-6) charmhelpers/contrib/openstack/amulet/deployment.py (+6/-2) charmhelpers/contrib/openstack/amulet/utils.py (+122/-3) charmhelpers/contrib/openstack/utils.py (+8/-1) |
To merge this branch: | bzr merge lp:~1chb1n/charm-helpers/heat-amulet |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Corey Bryant (community) | Approve | ||
James Page | Pending | ||
Review via email: mp+260723@code.launchpad.net |
Commit message
Description of the change
* Prep helpers for Wily/Liberty; resolve additional Vivid issues.
* Add heat client method.
* Add generic openstack resource status check and delete methods.
* Add create_
* Add file_to_url method.
* Add get_ubuntu_releases - uses distro_info to create a list property containing Ubuntu release codenames, in order of release.
* Add get_ubuntu_
* Flag validate_services with deprecation warning. This method should be eventually be deprecated in favor of the following, after charm tests are updated to use validate_
* Add validate_
* Add debug logging.
Tracking bug: https:/
This should be landed in conjunction with the new heat amulet tests @ https:/
## resource status and resource delete rationale:
New and existing amulet tests can be made with less code effort or duplication.
Rather than having numerous delete_XYZ methods for each of the numerous openstack types/objects, use delete_resource and pass a pointer. Similarly, instead of having wait/check/
Corey Bryant (corey.bryant) wrote : | # |
Corey Bryant (corey.bryant) wrote : | # |
Also there's one comment inline.
Corey Bryant (corey.bryant) wrote : | # |
Looks like there are also some lint errors.
Ryan Beisner (1chb1n) wrote : | # |
See that's why I ask you! Resource makes too much sense. Ack on the lint, already resolved locally. Also fully agree on the inline comment about comments. Thanks Corey!
Ryan Beisner (1chb1n) wrote : | # |
FYI - all amulet tests pass except Vivid, due to bug 1461535. Addressing @ charmhelpers as well as the heat branch.
Corey Bryant (corey.bryant) wrote : | # |
Looking good. I'd just remove the old validate_services() now and rename validate_
Ryan Beisner (1chb1n) wrote : | # |
I'd prefer to not do that actually. I'd like to take it smaller chunks.
This is what I'm aiming for:
1. Land heat amulet tests, at the same time unblocking vivid for all of the other amulet tests.
2. Enable and land vivid coverage in all of the charms with existing amulet tests, while also updating each of them.
3. Deprecate the old validate_services after it is confirmed to be not-used.
4. Add amulet tests for charms which don't yet have them.
Whaddaya think?
Ryan Beisner (1chb1n) wrote : | # |
Also, rmq reply inline.
Ryan Beisner (1chb1n) wrote : | # |
Oh, add to (3): Deprecate delete_image after the relevant tests have been updated to use delete_resource.
Corey Bryant (corey.bryant) wrote : | # |
> I'd prefer to not do that actually. I'd like to take it smaller chunks.
>
> This is what I'm aiming for:
>
> 1. Land heat amulet tests, at the same time unblocking vivid for all of the
> other amulet tests.
>
> 2. Enable and land vivid coverage in all of the charms with existing amulet
> tests, while also updating each of them.
>
> 3. Deprecate the old validate_services after it is confirmed to be not-used.
>
> 4. Add amulet tests for charms which don't yet have them.
>
> Whaddaya think?
I just prefer not to land code that's going to be pulled back out in a week. But I can't land to c-h anyway so it's not up to me.
Now that I think of it though, and more importantly, I don't know what the process is for deprecating functions that aren't openstack specific. Someone else might already be using them and some of these changes would break them. I wonder what the precedence is.
Ryan Beisner (1chb1n) wrote : | # |
I can definitely see your point, which raises more caution:
The original validate_services method isn't actually broken in any way. Misnamed, maybe, but not broken. It just runs commands and reports back pass/fail. If the test writer passes the proper service-checking (or other arbitrary) commands, it will happily run them. A more appropriate name for the method may have been something like run_these_
Maybe we shouldn't deprecate or change the behavior of validate_services at all, as there is no way to know who that will break.
I'd suggest that we:
leave validate_services untouched, not "fixed"
add validate_
discuss and handle the topic of deprecation separately, if at all.
Corey Bryant (corey.bryant) wrote : | # |
Good points. I think that sounds like a good plan, and keep your deprecation comments to steer folks to the new functions.
Ryan Beisner (1chb1n) wrote : | # |
Ok, thanks - let's proceed as such.
Just a reminder, the "create or get keypair" method will be movedto this MP once that's reworked from the heat MP.
- 385. By Ryan Beisner
-
Return validate_services to original functionality
Add create_or_get_ keypair
Update comments and docstrings - 386. By Ryan Beisner
-
Add Wily/Liberty awareness.
- 387. By Ryan Beisner
-
add file_to_url
Ryan Beisner (1chb1n) wrote : | # |
Ready for review.
Corey Bryant (corey.bryant) wrote : | # |
Looks good. 2 really minor inline comments.
- 388. By Ryan Beisner
-
light cleanup and comment correction
Preview Diff
1 | === modified file 'charmhelpers/contrib/amulet/utils.py' |
2 | --- charmhelpers/contrib/amulet/utils.py 2015-04-21 15:40:51 +0000 |
3 | +++ charmhelpers/contrib/amulet/utils.py 2015-06-11 19:47:33 +0000 |
4 | @@ -15,13 +15,15 @@ |
5 | # along with charm-helpers. If not, see <http://www.gnu.org/licenses/>. |
6 | |
7 | import ConfigParser |
8 | +import distro_info |
9 | import io |
10 | import logging |
11 | +import os |
12 | import re |
13 | +import six |
14 | import sys |
15 | import time |
16 | - |
17 | -import six |
18 | +import urlparse |
19 | |
20 | |
21 | class AmuletUtils(object): |
22 | @@ -33,6 +35,7 @@ |
23 | |
24 | def __init__(self, log_level=logging.ERROR): |
25 | self.log = self.get_logger(level=log_level) |
26 | + self.ubuntu_releases = self.get_ubuntu_releases() |
27 | |
28 | def get_logger(self, name="amulet-logger", level=logging.DEBUG): |
29 | """Get a logger object that will log to stdout.""" |
30 | @@ -70,12 +73,44 @@ |
31 | else: |
32 | return False |
33 | |
34 | + def get_ubuntu_release_from_sentry(self, sentry_unit): |
35 | + """Get Ubuntu release codename from sentry unit. |
36 | + |
37 | + :param sentry_unit: amulet sentry/service unit pointer |
38 | + :returns: list of strings - release codename, failure message |
39 | + """ |
40 | + msg = None |
41 | + cmd = 'lsb_release -cs' |
42 | + release, code = sentry_unit.run(cmd) |
43 | + if code == 0: |
44 | + self.log.debug('{} lsb_release: {}'.format( |
45 | + sentry_unit.info['unit_name'], release)) |
46 | + else: |
47 | + msg = ('{} `{}` returned {} ' |
48 | + '{}'.format(sentry_unit.info['unit_name'], |
49 | + cmd, release, code)) |
50 | + if release not in self.ubuntu_releases: |
51 | + msg = ("Release ({}) not found in Ubuntu releases " |
52 | + "({})".format(release, self.ubuntu_releases)) |
53 | + return release, msg |
54 | + |
55 | def validate_services(self, commands): |
56 | - """Validate services. |
57 | - |
58 | - Verify the specified services are running on the corresponding |
59 | + """Validate that lists of commands succeed on service units. Can be |
60 | + used to verify system services are running on the corresponding |
61 | service units. |
62 | - """ |
63 | + |
64 | + :param commands: dict with sentry keys and arbitrary command list values |
65 | + :returns: None if successful, Failure string message otherwise |
66 | + """ |
67 | + self.log.debug('Checking status of system services...') |
68 | + |
69 | + # /!\ DEPRECATION WARNING (beisner): |
70 | + # New and existing tests should be rewritten to use |
71 | + # validate_services_by_name() as it is aware of init systems. |
72 | + self.log.warn('/!\\ DEPRECATION WARNING: use ' |
73 | + 'validate_services_by_name instead of validate_services ' |
74 | + 'due to init system differences.') |
75 | + |
76 | for k, v in six.iteritems(commands): |
77 | for cmd in v: |
78 | output, code = k.run(cmd) |
79 | @@ -86,6 +121,41 @@ |
80 | return "command `{}` returned {}".format(cmd, str(code)) |
81 | return None |
82 | |
83 | + def validate_services_by_name(self, sentry_services): |
84 | + """Validate system service status by service name, automatically |
85 | + detecting init system based on Ubuntu release codename. |
86 | + |
87 | + :param sentry_services: dict with sentry keys and svc list values |
88 | + :returns: None if successful, Failure string message otherwise |
89 | + """ |
90 | + self.log.debug('Checking status of system services...') |
91 | + |
92 | + # Point at which systemd became a thing |
93 | + systemd_switch = self.ubuntu_releases.index('vivid') |
94 | + |
95 | + for sentry_unit, services_list in six.iteritems(sentry_services): |
96 | + # Get lsb_release codename from unit |
97 | + release, ret = self.get_ubuntu_release_from_sentry(sentry_unit) |
98 | + if ret: |
99 | + return ret |
100 | + |
101 | + for service_name in services_list: |
102 | + if (self.ubuntu_releases.index(release) >= systemd_switch or |
103 | + service_name == "rabbitmq-server"): |
104 | + # init is systemd |
105 | + cmd = 'sudo service {} status'.format(service_name) |
106 | + elif self.ubuntu_releases.index(release) < systemd_switch: |
107 | + # init is upstart |
108 | + cmd = 'sudo status {}'.format(service_name) |
109 | + |
110 | + output, code = sentry_unit.run(cmd) |
111 | + self.log.debug('{} `{}` returned ' |
112 | + '{}'.format(sentry_unit.info['unit_name'], |
113 | + cmd, code)) |
114 | + if code != 0: |
115 | + return "command `{}` returned {}".format(cmd, str(code)) |
116 | + return None |
117 | + |
118 | def _get_config(self, unit, filename): |
119 | """Get a ConfigParser object for parsing a unit's config file.""" |
120 | file_contents = unit.file_contents(filename) |
121 | @@ -104,6 +174,9 @@ |
122 | Verify that the specified section of the config file contains |
123 | the expected option key:value pairs. |
124 | """ |
125 | + self.log.debug('Validating config file data ({} in {} on {})' |
126 | + '...'.format(section, config_file, |
127 | + sentry_unit.info['unit_name'])) |
128 | config = self._get_config(sentry_unit, config_file) |
129 | |
130 | if section != 'DEFAULT' and not config.has_section(section): |
131 | @@ -321,3 +394,15 @@ |
132 | |
133 | def endpoint_error(self, name, data): |
134 | return 'unexpected endpoint data in {} - {}'.format(name, data) |
135 | + |
136 | + def get_ubuntu_releases(self): |
137 | + """Return a list of all Ubuntu releases in order of release.""" |
138 | + _d = distro_info.UbuntuDistroInfo() |
139 | + _release_list = _d.all |
140 | + self.log.debug('Ubuntu release list: {}'.format(_release_list)) |
141 | + return _release_list |
142 | + |
143 | + def file_to_url(self, file_rel_path): |
144 | + """Convert a relative file path to a file URL.""" |
145 | + _abs_path = os.path.abspath(file_rel_path) |
146 | + return urlparse.urlparse(_abs_path, scheme='file').geturl() |
147 | |
148 | === modified file 'charmhelpers/contrib/openstack/amulet/deployment.py' |
149 | --- charmhelpers/contrib/openstack/amulet/deployment.py 2015-04-21 15:40:51 +0000 |
150 | +++ charmhelpers/contrib/openstack/amulet/deployment.py 2015-06-11 19:47:33 +0000 |
151 | @@ -110,7 +110,8 @@ |
152 | (self.precise_essex, self.precise_folsom, self.precise_grizzly, |
153 | self.precise_havana, self.precise_icehouse, |
154 | self.trusty_icehouse, self.trusty_juno, self.utopic_juno, |
155 | - self.trusty_kilo, self.vivid_kilo) = range(10) |
156 | + self.trusty_kilo, self.vivid_kilo, self.trusty_liberty, |
157 | + self.wily_liberty) = range(12) |
158 | |
159 | releases = { |
160 | ('precise', None): self.precise_essex, |
161 | @@ -121,8 +122,10 @@ |
162 | ('trusty', None): self.trusty_icehouse, |
163 | ('trusty', 'cloud:trusty-juno'): self.trusty_juno, |
164 | ('trusty', 'cloud:trusty-kilo'): self.trusty_kilo, |
165 | + ('trusty', 'cloud:trusty-liberty'): self.trusty_liberty, |
166 | ('utopic', None): self.utopic_juno, |
167 | - ('vivid', None): self.vivid_kilo} |
168 | + ('vivid', None): self.vivid_kilo, |
169 | + ('wily', None): self.wily_liberty} |
170 | return releases[(self.series, self.openstack)] |
171 | |
172 | def _get_openstack_release_string(self): |
173 | @@ -138,6 +141,7 @@ |
174 | ('trusty', 'icehouse'), |
175 | ('utopic', 'juno'), |
176 | ('vivid', 'kilo'), |
177 | + ('wily', 'liberty'), |
178 | ]) |
179 | if self.openstack: |
180 | os_origin = self.openstack.split(':')[1] |
181 | |
182 | === modified file 'charmhelpers/contrib/openstack/amulet/utils.py' |
183 | --- charmhelpers/contrib/openstack/amulet/utils.py 2015-01-22 06:06:03 +0000 |
184 | +++ charmhelpers/contrib/openstack/amulet/utils.py 2015-06-11 19:47:33 +0000 |
185 | @@ -16,15 +16,15 @@ |
186 | |
187 | import logging |
188 | import os |
189 | +import six |
190 | import time |
191 | import urllib |
192 | |
193 | import glanceclient.v1.client as glance_client |
194 | +import heatclient.v1.client as heat_client |
195 | import keystoneclient.v2_0 as keystone_client |
196 | import novaclient.v1_1.client as nova_client |
197 | |
198 | -import six |
199 | - |
200 | from charmhelpers.contrib.amulet.utils import ( |
201 | AmuletUtils |
202 | ) |
203 | @@ -37,7 +37,7 @@ |
204 | """OpenStack amulet utilities. |
205 | |
206 | This class inherits from AmuletUtils and has additional support |
207 | - that is specifically for use by OpenStack charms. |
208 | + that is specifically for use by OpenStack charm tests. |
209 | """ |
210 | |
211 | def __init__(self, log_level=ERROR): |
212 | @@ -51,6 +51,8 @@ |
213 | Validate actual endpoint data vs expected endpoint data. The ports |
214 | are used to find the matching endpoint. |
215 | """ |
216 | + self.log.debug('Validating endpoint data...') |
217 | + self.log.debug('actual: {}'.format(repr(endpoints))) |
218 | found = False |
219 | for ep in endpoints: |
220 | self.log.debug('endpoint: {}'.format(repr(ep))) |
221 | @@ -77,6 +79,7 @@ |
222 | Validate a list of actual service catalog endpoints vs a list of |
223 | expected service catalog endpoints. |
224 | """ |
225 | + self.log.debug('Validating service catalog endpoint data...') |
226 | self.log.debug('actual: {}'.format(repr(actual))) |
227 | for k, v in six.iteritems(expected): |
228 | if k in actual: |
229 | @@ -93,6 +96,7 @@ |
230 | Validate a list of actual tenant data vs list of expected tenant |
231 | data. |
232 | """ |
233 | + self.log.debug('Validating tenant data...') |
234 | self.log.debug('actual: {}'.format(repr(actual))) |
235 | for e in expected: |
236 | found = False |
237 | @@ -114,6 +118,7 @@ |
238 | Validate a list of actual role data vs a list of expected role |
239 | data. |
240 | """ |
241 | + self.log.debug('Validating role data...') |
242 | self.log.debug('actual: {}'.format(repr(actual))) |
243 | for e in expected: |
244 | found = False |
245 | @@ -134,6 +139,7 @@ |
246 | Validate a list of actual user data vs a list of expected user |
247 | data. |
248 | """ |
249 | + self.log.debug('Validating user data...') |
250 | self.log.debug('actual: {}'.format(repr(actual))) |
251 | for e in expected: |
252 | found = False |
253 | @@ -155,17 +161,20 @@ |
254 | |
255 | Validate a list of actual flavors vs a list of expected flavors. |
256 | """ |
257 | + self.log.debug('Validating flavor data...') |
258 | self.log.debug('actual: {}'.format(repr(actual))) |
259 | act = [a.name for a in actual] |
260 | return self._validate_list_data(expected, act) |
261 | |
262 | def tenant_exists(self, keystone, tenant): |
263 | """Return True if tenant exists.""" |
264 | + self.log.debug('Checking if tenant exists ({})...'.format(tenant)) |
265 | return tenant in [t.name for t in keystone.tenants.list()] |
266 | |
267 | def authenticate_keystone_admin(self, keystone_sentry, user, password, |
268 | tenant): |
269 | """Authenticates admin user with the keystone admin endpoint.""" |
270 | + self.log.debug('Authenticating keystone admin...') |
271 | unit = keystone_sentry |
272 | service_ip = unit.relation('shared-db', |
273 | 'mysql:shared-db')['private-address'] |
274 | @@ -175,6 +184,7 @@ |
275 | |
276 | def authenticate_keystone_user(self, keystone, user, password, tenant): |
277 | """Authenticates a regular user with the keystone public endpoint.""" |
278 | + self.log.debug('Authenticating keystone user ({})...'.format(user)) |
279 | ep = keystone.service_catalog.url_for(service_type='identity', |
280 | endpoint_type='publicURL') |
281 | return keystone_client.Client(username=user, password=password, |
282 | @@ -182,12 +192,21 @@ |
283 | |
284 | def authenticate_glance_admin(self, keystone): |
285 | """Authenticates admin user with glance.""" |
286 | + self.log.debug('Authenticating glance admin...') |
287 | ep = keystone.service_catalog.url_for(service_type='image', |
288 | endpoint_type='adminURL') |
289 | return glance_client.Client(ep, token=keystone.auth_token) |
290 | |
291 | + def authenticate_heat_admin(self, keystone): |
292 | + """Authenticates the admin user with heat.""" |
293 | + self.log.debug('Authenticating heat admin...') |
294 | + ep = keystone.service_catalog.url_for(service_type='orchestration', |
295 | + endpoint_type='publicURL') |
296 | + return heat_client.Client(endpoint=ep, token=keystone.auth_token) |
297 | + |
298 | def authenticate_nova_user(self, keystone, user, password, tenant): |
299 | """Authenticates a regular user with nova-api.""" |
300 | + self.log.debug('Authenticating nova user ({})...'.format(user)) |
301 | ep = keystone.service_catalog.url_for(service_type='identity', |
302 | endpoint_type='publicURL') |
303 | return nova_client.Client(username=user, api_key=password, |
304 | @@ -195,6 +214,7 @@ |
305 | |
306 | def create_cirros_image(self, glance, image_name): |
307 | """Download the latest cirros image and upload it to glance.""" |
308 | + self.log.debug('Creating glance image ({})...'.format(image_name)) |
309 | http_proxy = os.getenv('AMULET_HTTP_PROXY') |
310 | self.log.debug('AMULET_HTTP_PROXY: {}'.format(http_proxy)) |
311 | if http_proxy: |
312 | @@ -235,6 +255,11 @@ |
313 | |
314 | def delete_image(self, glance, image): |
315 | """Delete the specified image.""" |
316 | + |
317 | + # /!\ DEPRECATION WARNING |
318 | + self.log.warn('/!\\ DEPRECATION WARNING: use ' |
319 | + 'delete_resource instead of delete_image.') |
320 | + self.log.debug('Deleting glance image ({})...'.format(image)) |
321 | num_before = len(list(glance.images.list())) |
322 | glance.images.delete(image) |
323 | |
324 | @@ -254,6 +279,8 @@ |
325 | |
326 | def create_instance(self, nova, image_name, instance_name, flavor): |
327 | """Create the specified instance.""" |
328 | + self.log.debug('Creating instance ' |
329 | + '({}|{}|{})'.format(instance_name, image_name, flavor)) |
330 | image = nova.images.find(name=image_name) |
331 | flavor = nova.flavors.find(name=flavor) |
332 | instance = nova.servers.create(name=instance_name, image=image, |
333 | @@ -276,6 +303,11 @@ |
334 | |
335 | def delete_instance(self, nova, instance): |
336 | """Delete the specified instance.""" |
337 | + |
338 | + # /!\ DEPRECATION WARNING |
339 | + self.log.warn('/!\\ DEPRECATION WARNING: use ' |
340 | + 'delete_resource instead of delete_instance.') |
341 | + self.log.debug('Deleting instance ({})...'.format(instance)) |
342 | num_before = len(list(nova.servers.list())) |
343 | nova.servers.delete(instance) |
344 | |
345 | @@ -292,3 +324,90 @@ |
346 | return False |
347 | |
348 | return True |
349 | + |
350 | + def create_or_get_keypair(self, nova, keypair_name="testkey"): |
351 | + """Create a new keypair, or return pointer if it already exists.""" |
352 | + try: |
353 | + _keypair = nova.keypairs.get(keypair_name) |
354 | + self.log.debug('Keypair ({}) already exists, ' |
355 | + 'using it.'.format(keypair_name)) |
356 | + return _keypair |
357 | + except: |
358 | + self.log.debug('Keypair ({}) does not exist, ' |
359 | + 'creating it.'.format(keypair_name)) |
360 | + |
361 | + _keypair = nova.keypairs.create(name=keypair_name) |
362 | + return _keypair |
363 | + |
364 | + def delete_resource(self, resource, resource_id, |
365 | + msg="resource", max_wait=120): |
366 | + """Delete one openstack resource, such as one instance, keypair, |
367 | + image, volume, stack, etc., and confirm deletion within max wait time. |
368 | + |
369 | + :param resource: pointer to os resource type, ex:glance_client.images |
370 | + :param resource_id: unique name or id for the openstack resource |
371 | + :param msg: text to identify purpose in logging |
372 | + :param max_wait: maximum wait time in seconds |
373 | + :returns: True if successful, otherwise False |
374 | + """ |
375 | + num_before = len(list(resource.list())) |
376 | + resource.delete(resource_id) |
377 | + |
378 | + tries = 0 |
379 | + num_after = len(list(resource.list())) |
380 | + while num_after != (num_before - 1) and tries < (max_wait / 4): |
381 | + self.log.debug('{} delete check: ' |
382 | + '{} [{}:{}] {}'.format(msg, tries, |
383 | + num_before, |
384 | + num_after, |
385 | + resource_id)) |
386 | + time.sleep(4) |
387 | + num_after = len(list(resource.list())) |
388 | + tries += 1 |
389 | + |
390 | + self.log.debug('{}: expected, actual count = {}, ' |
391 | + '{}'.format(msg, num_before - 1, num_after)) |
392 | + |
393 | + if num_after == (num_before - 1): |
394 | + return True |
395 | + else: |
396 | + self.log.error('{} delete timed out'.format(msg)) |
397 | + return False |
398 | + |
399 | + def resource_reaches_status(self, resource, resource_id, |
400 | + expected_stat='available', |
401 | + msg='resource', max_wait=120): |
402 | + """Wait for an openstack resources status to reach an |
403 | + expected status within a specified time. Useful to confirm that |
404 | + nova instances, cinder vols, snapshots, glance images, heat stacks |
405 | + and other resources eventually reach the expected status. |
406 | + |
407 | + :param resource: pointer to os resource type, ex: heat_client.stacks |
408 | + :param resource_id: unique id for the openstack resource |
409 | + :param expected_stat: status to expect resource to reach |
410 | + :param msg: text to identify purpose in logging |
411 | + :param max_wait: maximum wait time in seconds |
412 | + :returns: True if successful, False if status is not reached |
413 | + """ |
414 | + |
415 | + tries = 0 |
416 | + resource_stat = resource.get(resource_id).status |
417 | + while resource_stat != expected_stat and tries < (max_wait / 4): |
418 | + self.log.debug('{} status check: ' |
419 | + '{} [{}:{}] {}'.format(msg, tries, |
420 | + resource_stat, |
421 | + expected_stat, |
422 | + resource_id)) |
423 | + time.sleep(4) |
424 | + resource_stat = resource.get(resource_id).status |
425 | + tries += 1 |
426 | + |
427 | + self.log.debug('{}: expected, actual status = {}, ' |
428 | + '{}'.format(msg, resource_stat, expected_stat)) |
429 | + |
430 | + if resource_stat == expected_stat: |
431 | + return True |
432 | + else: |
433 | + self.log.debug('{} never reached expected status: ' |
434 | + '{}'.format(resource_id, expected_stat)) |
435 | + return False |
436 | |
437 | === modified file 'charmhelpers/contrib/openstack/utils.py' |
438 | --- charmhelpers/contrib/openstack/utils.py 2015-05-11 18:53:44 +0000 |
439 | +++ charmhelpers/contrib/openstack/utils.py 2015-06-11 19:47:33 +0000 |
440 | @@ -79,6 +79,7 @@ |
441 | ('trusty', 'icehouse'), |
442 | ('utopic', 'juno'), |
443 | ('vivid', 'kilo'), |
444 | + ('wily', 'liberty'), |
445 | ]) |
446 | |
447 | |
448 | @@ -91,6 +92,7 @@ |
449 | ('2014.1', 'icehouse'), |
450 | ('2014.2', 'juno'), |
451 | ('2015.1', 'kilo'), |
452 | + ('2015.2', 'liberty'), |
453 | ]) |
454 | |
455 | # The ugly duckling |
456 | @@ -113,6 +115,7 @@ |
457 | ('2.2.0', 'juno'), |
458 | ('2.2.1', 'kilo'), |
459 | ('2.2.2', 'kilo'), |
460 | + ('2.3.0', 'liberty'), |
461 | ]) |
462 | |
463 | DEFAULT_LOOPBACK_SIZE = '5G' |
464 | @@ -321,6 +324,9 @@ |
465 | 'kilo': 'trusty-updates/kilo', |
466 | 'kilo/updates': 'trusty-updates/kilo', |
467 | 'kilo/proposed': 'trusty-proposed/kilo', |
468 | + 'liberty': 'trusty-updates/liberty', |
469 | + 'liberty/updates': 'trusty-updates/liberty', |
470 | + 'liberty/proposed': 'trusty-proposed/liberty', |
471 | } |
472 | |
473 | try: |
474 | @@ -641,7 +647,8 @@ |
475 | subprocess.check_call(cmd) |
476 | except subprocess.CalledProcessError: |
477 | package = os.path.basename(package_dir) |
478 | - error_out("Error updating {} from global-requirements.txt".format(package)) |
479 | + error_out("Error updating {} from " |
480 | + "global-requirements.txt".format(package)) |
481 | os.chdir(orig_dir) |
482 | |
483 |
The changes make sense to me. How about 'resource' instead of 'thing'? Something like delete_ openstack_ resource( ) seems like it would be understandable if someone was reading through code.