Merge lp:~corey.bryant/charms/trusty/keystone/sync-charm-helpers2 into lp:~openstack-charmers-archive/charms/trusty/keystone/next

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

Subscribers

People subscribed via source and target branches