Merge lp:~corey.bryant/charms/trusty/nova-cloud-controller/sync-charm-helpers into lp:~openstack-charmers-archive/charms/trusty/nova-cloud-controller/next

Proposed by Corey Bryant
Status: Merged
Merged at revision: 90
Proposed branch: lp:~corey.bryant/charms/trusty/nova-cloud-controller/sync-charm-helpers
Merge into: lp:~openstack-charmers-archive/charms/trusty/nova-cloud-controller/next
Diff against target: 689 lines (+265/-89)
7 files modified
Makefile (+1/-1)
hooks/charmhelpers/contrib/openstack/amulet/deployment.py (+13/-7)
hooks/charmhelpers/contrib/openstack/amulet/utils.py (+86/-20)
tests/charmhelpers/contrib/amulet/deployment.py (+20/-7)
tests/charmhelpers/contrib/amulet/utils.py (+46/-27)
tests/charmhelpers/contrib/openstack/amulet/deployment.py (+13/-7)
tests/charmhelpers/contrib/openstack/amulet/utils.py (+86/-20)
To merge this branch: bzr merge lp:~corey.bryant/charms/trusty/nova-cloud-controller/sync-charm-helpers
Reviewer Review Type Date Requested Status
OpenStack Charmers Pending
Review via email: mp+228879@code.launchpad.net

Description of the change

The amulet tests are broken without this.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2014-07-28 14:41:41 +0000
3+++ Makefile 2014-07-30 15:24:19 +0000
4@@ -20,7 +20,7 @@
5 # https://bugs.launchpad.net/amulet/+bug/1320357
6 @juju test -v -p AMULET_HTTP_PROXY
7
8-sync:
9+sync: bin/charm_helpers_sync.py
10 @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-hooks.yaml
11 @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-tests.yaml
12
13
14=== modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py'
15--- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-28 11:38:42 +0000
16+++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-30 15:24:19 +0000
17@@ -4,8 +4,11 @@
18
19
20 class OpenStackAmuletDeployment(AmuletDeployment):
21- """This class inherits from AmuletDeployment and has additional support
22- that is specifically for use by OpenStack charms."""
23+ """OpenStack amulet deployment.
24+
25+ This class inherits from AmuletDeployment and has additional support
26+ that is specifically for use by OpenStack charms.
27+ """
28
29 def __init__(self, series=None, openstack=None, source=None):
30 """Initialize the deployment environment."""
31@@ -40,11 +43,14 @@
32 self.d.configure(service, config)
33
34 def _get_openstack_release(self):
35- """Return an integer representing the enum value of the openstack
36- release."""
37- self.precise_essex, self.precise_folsom, self.precise_grizzly, \
38- self.precise_havana, self.precise_icehouse, \
39- self.trusty_icehouse = range(6)
40+ """Get openstack release.
41+
42+ Return an integer representing the enum value of the openstack
43+ release.
44+ """
45+ (self.precise_essex, self.precise_folsom, self.precise_grizzly,
46+ self.precise_havana, self.precise_icehouse,
47+ self.trusty_icehouse) = range(6)
48 releases = {
49 ('precise', None): self.precise_essex,
50 ('precise', 'cloud:precise-folsom'): self.precise_folsom,
51
52=== modified file 'hooks/charmhelpers/contrib/openstack/amulet/utils.py'
53--- hooks/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-28 11:38:42 +0000
54+++ hooks/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-30 15:24:19 +0000
55@@ -16,8 +16,11 @@
56
57
58 class OpenStackAmuletUtils(AmuletUtils):
59- """This class inherits from AmuletUtils and has additional support
60- that is specifically for use by OpenStack charms."""
61+ """OpenStack amulet utilities.
62+
63+ This class inherits from AmuletUtils and has additional support
64+ that is specifically for use by OpenStack charms.
65+ """
66
67 def __init__(self, log_level=ERROR):
68 """Initialize the deployment environment."""
69@@ -25,13 +28,17 @@
70
71 def validate_endpoint_data(self, endpoints, admin_port, internal_port,
72 public_port, expected):
73- """Validate actual endpoint data vs expected endpoint data. The ports
74- are used to find the matching endpoint."""
75+ """Validate endpoint data.
76+
77+ Validate actual endpoint data vs expected endpoint data. The ports
78+ are used to find the matching endpoint.
79+ """
80 found = False
81 for ep in endpoints:
82 self.log.debug('endpoint: {}'.format(repr(ep)))
83- if admin_port in ep.adminurl and internal_port in ep.internalurl \
84- and public_port in ep.publicurl:
85+ if (admin_port in ep.adminurl and
86+ internal_port in ep.internalurl and
87+ public_port in ep.publicurl):
88 found = True
89 actual = {'id': ep.id,
90 'region': ep.region,
91@@ -47,8 +54,11 @@
92 return 'endpoint not found'
93
94 def validate_svc_catalog_endpoint_data(self, expected, actual):
95- """Validate a list of actual service catalog endpoints vs a list of
96- expected service catalog endpoints."""
97+ """Validate service catalog endpoint data.
98+
99+ Validate a list of actual service catalog endpoints vs a list of
100+ expected service catalog endpoints.
101+ """
102 self.log.debug('actual: {}'.format(repr(actual)))
103 for k, v in expected.iteritems():
104 if k in actual:
105@@ -60,8 +70,11 @@
106 return ret
107
108 def validate_tenant_data(self, expected, actual):
109- """Validate a list of actual tenant data vs list of expected tenant
110- data."""
111+ """Validate tenant data.
112+
113+ Validate a list of actual tenant data vs list of expected tenant
114+ data.
115+ """
116 self.log.debug('actual: {}'.format(repr(actual)))
117 for e in expected:
118 found = False
119@@ -78,8 +91,11 @@
120 return ret
121
122 def validate_role_data(self, expected, actual):
123- """Validate a list of actual role data vs a list of expected role
124- data."""
125+ """Validate role data.
126+
127+ Validate a list of actual role data vs a list of expected role
128+ data.
129+ """
130 self.log.debug('actual: {}'.format(repr(actual)))
131 for e in expected:
132 found = False
133@@ -95,8 +111,11 @@
134 return ret
135
136 def validate_user_data(self, expected, actual):
137- """Validate a list of actual user data vs a list of expected user
138- data."""
139+ """Validate user data.
140+
141+ Validate a list of actual user data vs a list of expected user
142+ data.
143+ """
144 self.log.debug('actual: {}'.format(repr(actual)))
145 for e in expected:
146 found = False
147@@ -114,21 +133,24 @@
148 return ret
149
150 def validate_flavor_data(self, expected, actual):
151- """Validate a list of actual flavors vs a list of expected flavors."""
152+ """Validate flavor data.
153+
154+ Validate a list of actual flavors vs a list of expected flavors.
155+ """
156 self.log.debug('actual: {}'.format(repr(actual)))
157 act = [a.name for a in actual]
158 return self._validate_list_data(expected, act)
159
160 def tenant_exists(self, keystone, tenant):
161- """Return True if tenant exists"""
162+ """Return True if tenant exists."""
163 return tenant in [t.name for t in keystone.tenants.list()]
164
165 def authenticate_keystone_admin(self, keystone_sentry, user, password,
166 tenant):
167 """Authenticates admin user with the keystone admin endpoint."""
168- service_ip = \
169- keystone_sentry.relation('shared-db',
170- 'mysql:shared-db')['private-address']
171+ unit = keystone_sentry
172+ service_ip = unit.relation('shared-db',
173+ 'mysql:shared-db')['private-address']
174 ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))
175 return keystone_client.Client(username=user, password=password,
176 tenant_name=tenant, auth_url=ep)
177@@ -177,12 +199,40 @@
178 image = glance.images.create(name=image_name, is_public=True,
179 disk_format='qcow2',
180 container_format='bare', data=f)
181+ count = 1
182+ status = image.status
183+ while status != 'active' and count < 10:
184+ time.sleep(3)
185+ image = glance.images.get(image.id)
186+ status = image.status
187+ self.log.debug('image status: {}'.format(status))
188+ count += 1
189+
190+ if status != 'active':
191+ self.log.error('image creation timed out')
192+ return None
193+
194 return image
195
196 def delete_image(self, glance, image):
197 """Delete the specified image."""
198+ num_before = len(list(glance.images.list()))
199 glance.images.delete(image)
200
201+ count = 1
202+ num_after = len(list(glance.images.list()))
203+ while num_after != (num_before - 1) and count < 10:
204+ time.sleep(3)
205+ num_after = len(list(glance.images.list()))
206+ self.log.debug('number of images: {}'.format(num_after))
207+ count += 1
208+
209+ if num_after != (num_before - 1):
210+ self.log.error('image deletion timed out')
211+ return False
212+
213+ return True
214+
215 def create_instance(self, nova, image_name, instance_name, flavor):
216 """Create the specified instance."""
217 image = nova.images.find(name=image_name)
218@@ -199,11 +249,27 @@
219 self.log.debug('instance status: {}'.format(status))
220 count += 1
221
222- if status == 'BUILD':
223+ if status != 'ACTIVE':
224+ self.log.error('instance creation timed out')
225 return None
226
227 return instance
228
229 def delete_instance(self, nova, instance):
230 """Delete the specified instance."""
231+ num_before = len(list(nova.servers.list()))
232 nova.servers.delete(instance)
233+
234+ count = 1
235+ num_after = len(list(nova.servers.list()))
236+ while num_after != (num_before - 1) and count < 10:
237+ time.sleep(3)
238+ num_after = len(list(nova.servers.list()))
239+ self.log.debug('number of instances: {}'.format(num_after))
240+ count += 1
241+
242+ if num_after != (num_before - 1):
243+ self.log.error('instance deletion timed out')
244+ return False
245+
246+ return True
247
248=== modified file 'tests/charmhelpers/contrib/amulet/deployment.py'
249--- tests/charmhelpers/contrib/amulet/deployment.py 2014-07-28 11:38:42 +0000
250+++ tests/charmhelpers/contrib/amulet/deployment.py 2014-07-30 15:24:19 +0000
251@@ -1,9 +1,14 @@
252 import amulet
253
254+import os
255+
256
257 class AmuletDeployment(object):
258- """This class provides generic Amulet deployment and test runner
259- methods."""
260+ """Amulet deployment.
261+
262+ This class provides generic Amulet deployment and test runner
263+ methods.
264+ """
265
266 def __init__(self, series=None):
267 """Initialize the deployment environment."""
268@@ -16,11 +21,19 @@
269 self.d = amulet.Deployment()
270
271 def _add_services(self, this_service, other_services):
272- """Add services to the deployment where this_service is the local charm
273+ """Add services.
274+
275+ Add services to the deployment where this_service is the local charm
276 that we're focused on testing and other_services are the other
277- charms that come from the charm store."""
278+ charms that come from the charm store.
279+ """
280 name, units = range(2)
281- self.this_service = this_service[name]
282+
283+ if this_service[name] != os.path.basename(os.getcwd()):
284+ s = this_service[name]
285+ msg = "The charm's root directory name needs to be {}".format(s)
286+ amulet.raise_status(amulet.FAIL, msg=msg)
287+
288 self.d.add(this_service[name], units=this_service[units])
289
290 for svc in other_services:
291@@ -45,10 +58,10 @@
292 """Deploy environment and wait for all hooks to finish executing."""
293 try:
294 self.d.setup()
295- self.d.sentry.wait()
296+ self.d.sentry.wait(timeout=900)
297 except amulet.helpers.TimeoutError:
298 amulet.raise_status(amulet.FAIL, msg="Deployment timed out")
299- except:
300+ except Exception:
301 raise
302
303 def run_tests(self):
304
305=== modified file 'tests/charmhelpers/contrib/amulet/utils.py'
306--- tests/charmhelpers/contrib/amulet/utils.py 2014-07-28 11:38:42 +0000
307+++ tests/charmhelpers/contrib/amulet/utils.py 2014-07-30 15:24:19 +0000
308@@ -3,12 +3,15 @@
309 import logging
310 import re
311 import sys
312-from time import sleep
313+import time
314
315
316 class AmuletUtils(object):
317- """This class provides common utility functions that are used by Amulet
318- tests."""
319+ """Amulet utilities.
320+
321+ This class provides common utility functions that are used by Amulet
322+ tests.
323+ """
324
325 def __init__(self, log_level=logging.ERROR):
326 self.log = self.get_logger(level=log_level)
327@@ -17,8 +20,8 @@
328 """Get a logger object that will log to stdout."""
329 log = logging
330 logger = log.getLogger(name)
331- fmt = \
332- log.Formatter("%(asctime)s %(funcName)s %(levelname)s: %(message)s")
333+ fmt = log.Formatter("%(asctime)s %(funcName)s "
334+ "%(levelname)s: %(message)s")
335
336 handler = log.StreamHandler(stream=sys.stdout)
337 handler.setLevel(level)
338@@ -38,7 +41,7 @@
339 def valid_url(self, url):
340 p = re.compile(
341 r'^(?:http|ftp)s?://'
342- r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # flake8: noqa
343+ r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # noqa
344 r'localhost|'
345 r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
346 r'(?::\d+)?'
347@@ -50,8 +53,11 @@
348 return False
349
350 def validate_services(self, commands):
351- """Verify the specified services are running on the corresponding
352- service units."""
353+ """Validate services.
354+
355+ Verify the specified services are running on the corresponding
356+ service units.
357+ """
358 for k, v in commands.iteritems():
359 for cmd in v:
360 output, code = k.run(cmd)
361@@ -66,9 +72,13 @@
362 config.readfp(io.StringIO(file_contents))
363 return config
364
365- def validate_config_data(self, sentry_unit, config_file, section, expected):
366- """Verify that the specified section of the config file contains
367- the expected option key:value pairs."""
368+ def validate_config_data(self, sentry_unit, config_file, section,
369+ expected):
370+ """Validate config file data.
371+
372+ Verify that the specified section of the config file contains
373+ the expected option key:value pairs.
374+ """
375 config = self._get_config(sentry_unit, config_file)
376
377 if section != 'DEFAULT' and not config.has_section(section):
378@@ -78,20 +88,23 @@
379 if not config.has_option(section, k):
380 return "section [{}] is missing option {}".format(section, k)
381 if config.get(section, k) != expected[k]:
382- return "section [{}] {}:{} != expected {}:{}".format(section,
383- k, config.get(section, k), k, expected[k])
384+ return "section [{}] {}:{} != expected {}:{}".format(
385+ section, k, config.get(section, k), k, expected[k])
386 return None
387
388 def _validate_dict_data(self, expected, actual):
389- """Compare expected dictionary data vs actual dictionary data.
390+ """Validate dictionary data.
391+
392+ Compare expected dictionary data vs actual dictionary data.
393 The values in the 'expected' dictionary can be strings, bools, ints,
394 longs, or can be a function that evaluate a variable and returns a
395- bool."""
396+ bool.
397+ """
398 for k, v in expected.iteritems():
399 if k in actual:
400- if isinstance(v, basestring) or \
401- isinstance(v, bool) or \
402- isinstance(v, (int, long)):
403+ if (isinstance(v, basestring) or
404+ isinstance(v, bool) or
405+ isinstance(v, (int, long))):
406 if v != actual[k]:
407 return "{}:{}".format(k, actual[k])
408 elif not v(actual[k]):
409@@ -114,7 +127,7 @@
410 return None
411
412 def not_null(self, string):
413- if string != None:
414+ if string is not None:
415 return True
416 else:
417 return False
418@@ -128,9 +141,12 @@
419 return sentry_unit.directory_stat(directory)['mtime']
420
421 def _get_proc_start_time(self, sentry_unit, service, pgrep_full=False):
422- """Determine start time of the process based on the last modification
423+ """Get process' start time.
424+
425+ Determine start time of the process based on the last modification
426 time of the /proc/pid directory. If pgrep_full is True, the process
427- name is matched against the full command line."""
428+ name is matched against the full command line.
429+ """
430 if pgrep_full:
431 cmd = 'pgrep -o -f {}'.format(service)
432 else:
433@@ -139,13 +155,16 @@
434 return self._get_dir_mtime(sentry_unit, proc_dir)
435
436 def service_restarted(self, sentry_unit, service, filename,
437- pgrep_full=False):
438- """Compare a service's start time vs a file's last modification time
439+ pgrep_full=False, sleep_time=20):
440+ """Check if service was restarted.
441+
442+ Compare a service's start time vs a file's last modification time
443 (such as a config file for that service) to determine if the service
444- has been restarted."""
445- sleep(10)
446- if self._get_proc_start_time(sentry_unit, service, pgrep_full) >= \
447- self._get_file_mtime(sentry_unit, filename):
448+ has been restarted.
449+ """
450+ time.sleep(sleep_time)
451+ if (self._get_proc_start_time(sentry_unit, service, pgrep_full) >=
452+ self._get_file_mtime(sentry_unit, filename)):
453 return True
454 else:
455 return False
456
457=== modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py'
458--- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-28 11:38:42 +0000
459+++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-30 15:24:19 +0000
460@@ -4,8 +4,11 @@
461
462
463 class OpenStackAmuletDeployment(AmuletDeployment):
464- """This class inherits from AmuletDeployment and has additional support
465- that is specifically for use by OpenStack charms."""
466+ """OpenStack amulet deployment.
467+
468+ This class inherits from AmuletDeployment and has additional support
469+ that is specifically for use by OpenStack charms.
470+ """
471
472 def __init__(self, series=None, openstack=None, source=None):
473 """Initialize the deployment environment."""
474@@ -40,11 +43,14 @@
475 self.d.configure(service, config)
476
477 def _get_openstack_release(self):
478- """Return an integer representing the enum value of the openstack
479- release."""
480- self.precise_essex, self.precise_folsom, self.precise_grizzly, \
481- self.precise_havana, self.precise_icehouse, \
482- self.trusty_icehouse = range(6)
483+ """Get openstack release.
484+
485+ Return an integer representing the enum value of the openstack
486+ release.
487+ """
488+ (self.precise_essex, self.precise_folsom, self.precise_grizzly,
489+ self.precise_havana, self.precise_icehouse,
490+ self.trusty_icehouse) = range(6)
491 releases = {
492 ('precise', None): self.precise_essex,
493 ('precise', 'cloud:precise-folsom'): self.precise_folsom,
494
495=== modified file 'tests/charmhelpers/contrib/openstack/amulet/utils.py'
496--- tests/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-28 11:38:42 +0000
497+++ tests/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-30 15:24:19 +0000
498@@ -16,8 +16,11 @@
499
500
501 class OpenStackAmuletUtils(AmuletUtils):
502- """This class inherits from AmuletUtils and has additional support
503- that is specifically for use by OpenStack charms."""
504+ """OpenStack amulet utilities.
505+
506+ This class inherits from AmuletUtils and has additional support
507+ that is specifically for use by OpenStack charms.
508+ """
509
510 def __init__(self, log_level=ERROR):
511 """Initialize the deployment environment."""
512@@ -25,13 +28,17 @@
513
514 def validate_endpoint_data(self, endpoints, admin_port, internal_port,
515 public_port, expected):
516- """Validate actual endpoint data vs expected endpoint data. The ports
517- are used to find the matching endpoint."""
518+ """Validate endpoint data.
519+
520+ Validate actual endpoint data vs expected endpoint data. The ports
521+ are used to find the matching endpoint.
522+ """
523 found = False
524 for ep in endpoints:
525 self.log.debug('endpoint: {}'.format(repr(ep)))
526- if admin_port in ep.adminurl and internal_port in ep.internalurl \
527- and public_port in ep.publicurl:
528+ if (admin_port in ep.adminurl and
529+ internal_port in ep.internalurl and
530+ public_port in ep.publicurl):
531 found = True
532 actual = {'id': ep.id,
533 'region': ep.region,
534@@ -47,8 +54,11 @@
535 return 'endpoint not found'
536
537 def validate_svc_catalog_endpoint_data(self, expected, actual):
538- """Validate a list of actual service catalog endpoints vs a list of
539- expected service catalog endpoints."""
540+ """Validate service catalog endpoint data.
541+
542+ Validate a list of actual service catalog endpoints vs a list of
543+ expected service catalog endpoints.
544+ """
545 self.log.debug('actual: {}'.format(repr(actual)))
546 for k, v in expected.iteritems():
547 if k in actual:
548@@ -60,8 +70,11 @@
549 return ret
550
551 def validate_tenant_data(self, expected, actual):
552- """Validate a list of actual tenant data vs list of expected tenant
553- data."""
554+ """Validate tenant data.
555+
556+ Validate a list of actual tenant data vs list of expected tenant
557+ data.
558+ """
559 self.log.debug('actual: {}'.format(repr(actual)))
560 for e in expected:
561 found = False
562@@ -78,8 +91,11 @@
563 return ret
564
565 def validate_role_data(self, expected, actual):
566- """Validate a list of actual role data vs a list of expected role
567- data."""
568+ """Validate role data.
569+
570+ Validate a list of actual role data vs a list of expected role
571+ data.
572+ """
573 self.log.debug('actual: {}'.format(repr(actual)))
574 for e in expected:
575 found = False
576@@ -95,8 +111,11 @@
577 return ret
578
579 def validate_user_data(self, expected, actual):
580- """Validate a list of actual user data vs a list of expected user
581- data."""
582+ """Validate user data.
583+
584+ Validate a list of actual user data vs a list of expected user
585+ data.
586+ """
587 self.log.debug('actual: {}'.format(repr(actual)))
588 for e in expected:
589 found = False
590@@ -114,21 +133,24 @@
591 return ret
592
593 def validate_flavor_data(self, expected, actual):
594- """Validate a list of actual flavors vs a list of expected flavors."""
595+ """Validate flavor data.
596+
597+ Validate a list of actual flavors vs a list of expected flavors.
598+ """
599 self.log.debug('actual: {}'.format(repr(actual)))
600 act = [a.name for a in actual]
601 return self._validate_list_data(expected, act)
602
603 def tenant_exists(self, keystone, tenant):
604- """Return True if tenant exists"""
605+ """Return True if tenant exists."""
606 return tenant in [t.name for t in keystone.tenants.list()]
607
608 def authenticate_keystone_admin(self, keystone_sentry, user, password,
609 tenant):
610 """Authenticates admin user with the keystone admin endpoint."""
611- service_ip = \
612- keystone_sentry.relation('shared-db',
613- 'mysql:shared-db')['private-address']
614+ unit = keystone_sentry
615+ service_ip = unit.relation('shared-db',
616+ 'mysql:shared-db')['private-address']
617 ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))
618 return keystone_client.Client(username=user, password=password,
619 tenant_name=tenant, auth_url=ep)
620@@ -177,12 +199,40 @@
621 image = glance.images.create(name=image_name, is_public=True,
622 disk_format='qcow2',
623 container_format='bare', data=f)
624+ count = 1
625+ status = image.status
626+ while status != 'active' and count < 10:
627+ time.sleep(3)
628+ image = glance.images.get(image.id)
629+ status = image.status
630+ self.log.debug('image status: {}'.format(status))
631+ count += 1
632+
633+ if status != 'active':
634+ self.log.error('image creation timed out')
635+ return None
636+
637 return image
638
639 def delete_image(self, glance, image):
640 """Delete the specified image."""
641+ num_before = len(list(glance.images.list()))
642 glance.images.delete(image)
643
644+ count = 1
645+ num_after = len(list(glance.images.list()))
646+ while num_after != (num_before - 1) and count < 10:
647+ time.sleep(3)
648+ num_after = len(list(glance.images.list()))
649+ self.log.debug('number of images: {}'.format(num_after))
650+ count += 1
651+
652+ if num_after != (num_before - 1):
653+ self.log.error('image deletion timed out')
654+ return False
655+
656+ return True
657+
658 def create_instance(self, nova, image_name, instance_name, flavor):
659 """Create the specified instance."""
660 image = nova.images.find(name=image_name)
661@@ -199,11 +249,27 @@
662 self.log.debug('instance status: {}'.format(status))
663 count += 1
664
665- if status == 'BUILD':
666+ if status != 'ACTIVE':
667+ self.log.error('instance creation timed out')
668 return None
669
670 return instance
671
672 def delete_instance(self, nova, instance):
673 """Delete the specified instance."""
674+ num_before = len(list(nova.servers.list()))
675 nova.servers.delete(instance)
676+
677+ count = 1
678+ num_after = len(list(nova.servers.list()))
679+ while num_after != (num_before - 1) and count < 10:
680+ time.sleep(3)
681+ num_after = len(list(nova.servers.list()))
682+ self.log.debug('number of instances: {}'.format(num_after))
683+ count += 1
684+
685+ if num_after != (num_before - 1):
686+ self.log.error('instance deletion timed out')
687+ return False
688+
689+ return True

Subscribers

People subscribed via source and target branches