Sync with charm-helpers

This commit is contained in:
Corey Bryant 2014-07-30 15:20:13 +00:00
parent 74337766c1
commit f36eb2a121
6 changed files with 264 additions and 88 deletions

View File

@ -4,8 +4,11 @@ from charmhelpers.contrib.amulet.deployment import (
class OpenStackAmuletDeployment(AmuletDeployment): class OpenStackAmuletDeployment(AmuletDeployment):
"""This class inherits from AmuletDeployment and has additional support """OpenStack amulet deployment.
that is specifically for use by OpenStack charms."""
This class inherits from AmuletDeployment and has additional support
that is specifically for use by OpenStack charms.
"""
def __init__(self, series=None, openstack=None, source=None): def __init__(self, series=None, openstack=None, source=None):
"""Initialize the deployment environment.""" """Initialize the deployment environment."""
@ -40,11 +43,14 @@ class OpenStackAmuletDeployment(AmuletDeployment):
self.d.configure(service, config) self.d.configure(service, config)
def _get_openstack_release(self): def _get_openstack_release(self):
"""Return an integer representing the enum value of the openstack """Get openstack release.
release."""
self.precise_essex, self.precise_folsom, self.precise_grizzly, \ Return an integer representing the enum value of the openstack
self.precise_havana, self.precise_icehouse, \ release.
self.trusty_icehouse = range(6) """
(self.precise_essex, self.precise_folsom, self.precise_grizzly,
self.precise_havana, self.precise_icehouse,
self.trusty_icehouse) = range(6)
releases = { releases = {
('precise', None): self.precise_essex, ('precise', None): self.precise_essex,
('precise', 'cloud:precise-folsom'): self.precise_folsom, ('precise', 'cloud:precise-folsom'): self.precise_folsom,

View File

@ -16,8 +16,11 @@ ERROR = logging.ERROR
class OpenStackAmuletUtils(AmuletUtils): class OpenStackAmuletUtils(AmuletUtils):
"""This class inherits from AmuletUtils and has additional support """OpenStack amulet utilities.
that is specifically for use by OpenStack charms."""
This class inherits from AmuletUtils and has additional support
that is specifically for use by OpenStack charms.
"""
def __init__(self, log_level=ERROR): def __init__(self, log_level=ERROR):
"""Initialize the deployment environment.""" """Initialize the deployment environment."""
@ -25,13 +28,17 @@ class OpenStackAmuletUtils(AmuletUtils):
def validate_endpoint_data(self, endpoints, admin_port, internal_port, def validate_endpoint_data(self, endpoints, admin_port, internal_port,
public_port, expected): public_port, expected):
"""Validate actual endpoint data vs expected endpoint data. The ports """Validate endpoint data.
are used to find the matching endpoint."""
Validate actual endpoint data vs expected endpoint data. The ports
are used to find the matching endpoint.
"""
found = False found = False
for ep in endpoints: for ep in endpoints:
self.log.debug('endpoint: {}'.format(repr(ep))) self.log.debug('endpoint: {}'.format(repr(ep)))
if admin_port in ep.adminurl and internal_port in ep.internalurl \ if (admin_port in ep.adminurl and
and public_port in ep.publicurl: internal_port in ep.internalurl and
public_port in ep.publicurl):
found = True found = True
actual = {'id': ep.id, actual = {'id': ep.id,
'region': ep.region, 'region': ep.region,
@ -47,8 +54,11 @@ class OpenStackAmuletUtils(AmuletUtils):
return 'endpoint not found' return 'endpoint not found'
def validate_svc_catalog_endpoint_data(self, expected, actual): def validate_svc_catalog_endpoint_data(self, expected, actual):
"""Validate a list of actual service catalog endpoints vs a list of """Validate service catalog endpoint data.
expected service catalog endpoints."""
Validate a list of actual service catalog endpoints vs a list of
expected service catalog endpoints.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
for k, v in expected.iteritems(): for k, v in expected.iteritems():
if k in actual: if k in actual:
@ -60,8 +70,11 @@ class OpenStackAmuletUtils(AmuletUtils):
return ret return ret
def validate_tenant_data(self, expected, actual): def validate_tenant_data(self, expected, actual):
"""Validate a list of actual tenant data vs list of expected tenant """Validate tenant data.
data."""
Validate a list of actual tenant data vs list of expected tenant
data.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
for e in expected: for e in expected:
found = False found = False
@ -78,8 +91,11 @@ class OpenStackAmuletUtils(AmuletUtils):
return ret return ret
def validate_role_data(self, expected, actual): def validate_role_data(self, expected, actual):
"""Validate a list of actual role data vs a list of expected role """Validate role data.
data."""
Validate a list of actual role data vs a list of expected role
data.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
for e in expected: for e in expected:
found = False found = False
@ -95,8 +111,11 @@ class OpenStackAmuletUtils(AmuletUtils):
return ret return ret
def validate_user_data(self, expected, actual): def validate_user_data(self, expected, actual):
"""Validate a list of actual user data vs a list of expected user """Validate user data.
data."""
Validate a list of actual user data vs a list of expected user
data.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
for e in expected: for e in expected:
found = False found = False
@ -114,21 +133,24 @@ class OpenStackAmuletUtils(AmuletUtils):
return ret return ret
def validate_flavor_data(self, expected, actual): def validate_flavor_data(self, expected, actual):
"""Validate a list of actual flavors vs a list of expected flavors.""" """Validate flavor data.
Validate a list of actual flavors vs a list of expected flavors.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
act = [a.name for a in actual] act = [a.name for a in actual]
return self._validate_list_data(expected, act) return self._validate_list_data(expected, act)
def tenant_exists(self, keystone, tenant): def tenant_exists(self, keystone, tenant):
"""Return True if tenant exists""" """Return True if tenant exists."""
return tenant in [t.name for t in keystone.tenants.list()] return tenant in [t.name for t in keystone.tenants.list()]
def authenticate_keystone_admin(self, keystone_sentry, user, password, def authenticate_keystone_admin(self, keystone_sentry, user, password,
tenant): tenant):
"""Authenticates admin user with the keystone admin endpoint.""" """Authenticates admin user with the keystone admin endpoint."""
service_ip = \ unit = keystone_sentry
keystone_sentry.relation('shared-db', service_ip = unit.relation('shared-db',
'mysql:shared-db')['private-address'] 'mysql:shared-db')['private-address']
ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8')) ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))
return keystone_client.Client(username=user, password=password, return keystone_client.Client(username=user, password=password,
tenant_name=tenant, auth_url=ep) tenant_name=tenant, auth_url=ep)
@ -177,12 +199,40 @@ class OpenStackAmuletUtils(AmuletUtils):
image = glance.images.create(name=image_name, is_public=True, image = glance.images.create(name=image_name, is_public=True,
disk_format='qcow2', disk_format='qcow2',
container_format='bare', data=f) container_format='bare', data=f)
count = 1
status = image.status
while status != 'active' and count < 10:
time.sleep(3)
image = glance.images.get(image.id)
status = image.status
self.log.debug('image status: {}'.format(status))
count += 1
if status != 'active':
self.log.error('image creation timed out')
return None
return image return image
def delete_image(self, glance, image): def delete_image(self, glance, image):
"""Delete the specified image.""" """Delete the specified image."""
num_before = len(list(glance.images.list()))
glance.images.delete(image) glance.images.delete(image)
count = 1
num_after = len(list(glance.images.list()))
while num_after != (num_before - 1) and count < 10:
time.sleep(3)
num_after = len(list(glance.images.list()))
self.log.debug('number of images: {}'.format(num_after))
count += 1
if num_after != (num_before - 1):
self.log.error('image deletion timed out')
return False
return True
def create_instance(self, nova, image_name, instance_name, flavor): def create_instance(self, nova, image_name, instance_name, flavor):
"""Create the specified instance.""" """Create the specified instance."""
image = nova.images.find(name=image_name) image = nova.images.find(name=image_name)
@ -199,11 +249,27 @@ class OpenStackAmuletUtils(AmuletUtils):
self.log.debug('instance status: {}'.format(status)) self.log.debug('instance status: {}'.format(status))
count += 1 count += 1
if status == 'BUILD': if status != 'ACTIVE':
self.log.error('instance creation timed out')
return None return None
return instance return instance
def delete_instance(self, nova, instance): def delete_instance(self, nova, instance):
"""Delete the specified instance.""" """Delete the specified instance."""
num_before = len(list(nova.servers.list()))
nova.servers.delete(instance) nova.servers.delete(instance)
count = 1
num_after = len(list(nova.servers.list()))
while num_after != (num_before - 1) and count < 10:
time.sleep(3)
num_after = len(list(nova.servers.list()))
self.log.debug('number of instances: {}'.format(num_after))
count += 1
if num_after != (num_before - 1):
self.log.error('instance deletion timed out')
return False
return True

View File

@ -1,9 +1,14 @@
import amulet import amulet
import os
class AmuletDeployment(object): class AmuletDeployment(object):
"""This class provides generic Amulet deployment and test runner """Amulet deployment.
methods."""
This class provides generic Amulet deployment and test runner
methods.
"""
def __init__(self, series=None): def __init__(self, series=None):
"""Initialize the deployment environment.""" """Initialize the deployment environment."""
@ -16,11 +21,19 @@ class AmuletDeployment(object):
self.d = amulet.Deployment() self.d = amulet.Deployment()
def _add_services(self, this_service, other_services): def _add_services(self, this_service, other_services):
"""Add services to the deployment where this_service is the local charm """Add services.
Add services to the deployment where this_service is the local charm
that we're focused on testing and other_services are the other that we're focused on testing and other_services are the other
charms that come from the charm store.""" charms that come from the charm store.
"""
name, units = range(2) name, units = range(2)
self.this_service = this_service[name]
if this_service[name] != os.path.basename(os.getcwd()):
s = this_service[name]
msg = "The charm's root directory name needs to be {}".format(s)
amulet.raise_status(amulet.FAIL, msg=msg)
self.d.add(this_service[name], units=this_service[units]) self.d.add(this_service[name], units=this_service[units])
for svc in other_services: for svc in other_services:
@ -45,10 +58,10 @@ class AmuletDeployment(object):
"""Deploy environment and wait for all hooks to finish executing.""" """Deploy environment and wait for all hooks to finish executing."""
try: try:
self.d.setup() self.d.setup()
self.d.sentry.wait() self.d.sentry.wait(timeout=900)
except amulet.helpers.TimeoutError: except amulet.helpers.TimeoutError:
amulet.raise_status(amulet.FAIL, msg="Deployment timed out") amulet.raise_status(amulet.FAIL, msg="Deployment timed out")
except: except Exception:
raise raise
def run_tests(self): def run_tests(self):

View File

@ -3,12 +3,15 @@ import io
import logging import logging
import re import re
import sys import sys
from time import sleep import time
class AmuletUtils(object): class AmuletUtils(object):
"""This class provides common utility functions that are used by Amulet """Amulet utilities.
tests."""
This class provides common utility functions that are used by Amulet
tests.
"""
def __init__(self, log_level=logging.ERROR): def __init__(self, log_level=logging.ERROR):
self.log = self.get_logger(level=log_level) self.log = self.get_logger(level=log_level)
@ -17,8 +20,8 @@ class AmuletUtils(object):
"""Get a logger object that will log to stdout.""" """Get a logger object that will log to stdout."""
log = logging log = logging
logger = log.getLogger(name) logger = log.getLogger(name)
fmt = \ fmt = log.Formatter("%(asctime)s %(funcName)s "
log.Formatter("%(asctime)s %(funcName)s %(levelname)s: %(message)s") "%(levelname)s: %(message)s")
handler = log.StreamHandler(stream=sys.stdout) handler = log.StreamHandler(stream=sys.stdout)
handler.setLevel(level) handler.setLevel(level)
@ -38,7 +41,7 @@ class AmuletUtils(object):
def valid_url(self, url): def valid_url(self, url):
p = re.compile( p = re.compile(
r'^(?:http|ftp)s?://' r'^(?:http|ftp)s?://'
r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # flake8: noqa r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # noqa
r'localhost|' r'localhost|'
r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
r'(?::\d+)?' r'(?::\d+)?'
@ -50,8 +53,11 @@ class AmuletUtils(object):
return False return False
def validate_services(self, commands): def validate_services(self, commands):
"""Verify the specified services are running on the corresponding """Validate services.
service units."""
Verify the specified services are running on the corresponding
service units.
"""
for k, v in commands.iteritems(): for k, v in commands.iteritems():
for cmd in v: for cmd in v:
output, code = k.run(cmd) output, code = k.run(cmd)
@ -66,9 +72,13 @@ class AmuletUtils(object):
config.readfp(io.StringIO(file_contents)) config.readfp(io.StringIO(file_contents))
return config return config
def validate_config_data(self, sentry_unit, config_file, section, expected): def validate_config_data(self, sentry_unit, config_file, section,
"""Verify that the specified section of the config file contains expected):
the expected option key:value pairs.""" """Validate config file data.
Verify that the specified section of the config file contains
the expected option key:value pairs.
"""
config = self._get_config(sentry_unit, config_file) config = self._get_config(sentry_unit, config_file)
if section != 'DEFAULT' and not config.has_section(section): if section != 'DEFAULT' and not config.has_section(section):
@ -78,20 +88,23 @@ class AmuletUtils(object):
if not config.has_option(section, k): if not config.has_option(section, k):
return "section [{}] is missing option {}".format(section, k) return "section [{}] is missing option {}".format(section, k)
if config.get(section, k) != expected[k]: if config.get(section, k) != expected[k]:
return "section [{}] {}:{} != expected {}:{}".format(section, return "section [{}] {}:{} != expected {}:{}".format(
k, config.get(section, k), k, expected[k]) section, k, config.get(section, k), k, expected[k])
return None return None
def _validate_dict_data(self, expected, actual): def _validate_dict_data(self, expected, actual):
"""Compare expected dictionary data vs actual dictionary data. """Validate dictionary data.
Compare expected dictionary data vs actual dictionary data.
The values in the 'expected' dictionary can be strings, bools, ints, The values in the 'expected' dictionary can be strings, bools, ints,
longs, or can be a function that evaluate a variable and returns a longs, or can be a function that evaluate a variable and returns a
bool.""" bool.
"""
for k, v in expected.iteritems(): for k, v in expected.iteritems():
if k in actual: if k in actual:
if isinstance(v, basestring) or \ if (isinstance(v, basestring) or
isinstance(v, bool) or \ isinstance(v, bool) or
isinstance(v, (int, long)): isinstance(v, (int, long))):
if v != actual[k]: if v != actual[k]:
return "{}:{}".format(k, actual[k]) return "{}:{}".format(k, actual[k])
elif not v(actual[k]): elif not v(actual[k]):
@ -114,7 +127,7 @@ class AmuletUtils(object):
return None return None
def not_null(self, string): def not_null(self, string):
if string != None: if string is not None:
return True return True
else: else:
return False return False
@ -128,9 +141,12 @@ class AmuletUtils(object):
return sentry_unit.directory_stat(directory)['mtime'] return sentry_unit.directory_stat(directory)['mtime']
def _get_proc_start_time(self, sentry_unit, service, pgrep_full=False): def _get_proc_start_time(self, sentry_unit, service, pgrep_full=False):
"""Determine start time of the process based on the last modification """Get process' start time.
Determine start time of the process based on the last modification
time of the /proc/pid directory. If pgrep_full is True, the process time of the /proc/pid directory. If pgrep_full is True, the process
name is matched against the full command line.""" name is matched against the full command line.
"""
if pgrep_full: if pgrep_full:
cmd = 'pgrep -o -f {}'.format(service) cmd = 'pgrep -o -f {}'.format(service)
else: else:
@ -139,13 +155,16 @@ class AmuletUtils(object):
return self._get_dir_mtime(sentry_unit, proc_dir) return self._get_dir_mtime(sentry_unit, proc_dir)
def service_restarted(self, sentry_unit, service, filename, def service_restarted(self, sentry_unit, service, filename,
pgrep_full=False): pgrep_full=False, sleep_time=20):
"""Compare a service's start time vs a file's last modification time """Check if service was restarted.
Compare a service's start time vs a file's last modification time
(such as a config file for that service) to determine if the service (such as a config file for that service) to determine if the service
has been restarted.""" has been restarted.
sleep(10) """
if self._get_proc_start_time(sentry_unit, service, pgrep_full) >= \ time.sleep(sleep_time)
self._get_file_mtime(sentry_unit, filename): if (self._get_proc_start_time(sentry_unit, service, pgrep_full) >=
self._get_file_mtime(sentry_unit, filename)):
return True return True
else: else:
return False return False

View File

@ -4,8 +4,11 @@ from charmhelpers.contrib.amulet.deployment import (
class OpenStackAmuletDeployment(AmuletDeployment): class OpenStackAmuletDeployment(AmuletDeployment):
"""This class inherits from AmuletDeployment and has additional support """OpenStack amulet deployment.
that is specifically for use by OpenStack charms."""
This class inherits from AmuletDeployment and has additional support
that is specifically for use by OpenStack charms.
"""
def __init__(self, series=None, openstack=None, source=None): def __init__(self, series=None, openstack=None, source=None):
"""Initialize the deployment environment.""" """Initialize the deployment environment."""
@ -40,11 +43,14 @@ class OpenStackAmuletDeployment(AmuletDeployment):
self.d.configure(service, config) self.d.configure(service, config)
def _get_openstack_release(self): def _get_openstack_release(self):
"""Return an integer representing the enum value of the openstack """Get openstack release.
release."""
self.precise_essex, self.precise_folsom, self.precise_grizzly, \ Return an integer representing the enum value of the openstack
self.precise_havana, self.precise_icehouse, \ release.
self.trusty_icehouse = range(6) """
(self.precise_essex, self.precise_folsom, self.precise_grizzly,
self.precise_havana, self.precise_icehouse,
self.trusty_icehouse) = range(6)
releases = { releases = {
('precise', None): self.precise_essex, ('precise', None): self.precise_essex,
('precise', 'cloud:precise-folsom'): self.precise_folsom, ('precise', 'cloud:precise-folsom'): self.precise_folsom,

View File

@ -16,8 +16,11 @@ ERROR = logging.ERROR
class OpenStackAmuletUtils(AmuletUtils): class OpenStackAmuletUtils(AmuletUtils):
"""This class inherits from AmuletUtils and has additional support """OpenStack amulet utilities.
that is specifically for use by OpenStack charms."""
This class inherits from AmuletUtils and has additional support
that is specifically for use by OpenStack charms.
"""
def __init__(self, log_level=ERROR): def __init__(self, log_level=ERROR):
"""Initialize the deployment environment.""" """Initialize the deployment environment."""
@ -25,13 +28,17 @@ class OpenStackAmuletUtils(AmuletUtils):
def validate_endpoint_data(self, endpoints, admin_port, internal_port, def validate_endpoint_data(self, endpoints, admin_port, internal_port,
public_port, expected): public_port, expected):
"""Validate actual endpoint data vs expected endpoint data. The ports """Validate endpoint data.
are used to find the matching endpoint."""
Validate actual endpoint data vs expected endpoint data. The ports
are used to find the matching endpoint.
"""
found = False found = False
for ep in endpoints: for ep in endpoints:
self.log.debug('endpoint: {}'.format(repr(ep))) self.log.debug('endpoint: {}'.format(repr(ep)))
if admin_port in ep.adminurl and internal_port in ep.internalurl \ if (admin_port in ep.adminurl and
and public_port in ep.publicurl: internal_port in ep.internalurl and
public_port in ep.publicurl):
found = True found = True
actual = {'id': ep.id, actual = {'id': ep.id,
'region': ep.region, 'region': ep.region,
@ -47,8 +54,11 @@ class OpenStackAmuletUtils(AmuletUtils):
return 'endpoint not found' return 'endpoint not found'
def validate_svc_catalog_endpoint_data(self, expected, actual): def validate_svc_catalog_endpoint_data(self, expected, actual):
"""Validate a list of actual service catalog endpoints vs a list of """Validate service catalog endpoint data.
expected service catalog endpoints."""
Validate a list of actual service catalog endpoints vs a list of
expected service catalog endpoints.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
for k, v in expected.iteritems(): for k, v in expected.iteritems():
if k in actual: if k in actual:
@ -60,8 +70,11 @@ class OpenStackAmuletUtils(AmuletUtils):
return ret return ret
def validate_tenant_data(self, expected, actual): def validate_tenant_data(self, expected, actual):
"""Validate a list of actual tenant data vs list of expected tenant """Validate tenant data.
data."""
Validate a list of actual tenant data vs list of expected tenant
data.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
for e in expected: for e in expected:
found = False found = False
@ -78,8 +91,11 @@ class OpenStackAmuletUtils(AmuletUtils):
return ret return ret
def validate_role_data(self, expected, actual): def validate_role_data(self, expected, actual):
"""Validate a list of actual role data vs a list of expected role """Validate role data.
data."""
Validate a list of actual role data vs a list of expected role
data.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
for e in expected: for e in expected:
found = False found = False
@ -95,8 +111,11 @@ class OpenStackAmuletUtils(AmuletUtils):
return ret return ret
def validate_user_data(self, expected, actual): def validate_user_data(self, expected, actual):
"""Validate a list of actual user data vs a list of expected user """Validate user data.
data."""
Validate a list of actual user data vs a list of expected user
data.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
for e in expected: for e in expected:
found = False found = False
@ -114,21 +133,24 @@ class OpenStackAmuletUtils(AmuletUtils):
return ret return ret
def validate_flavor_data(self, expected, actual): def validate_flavor_data(self, expected, actual):
"""Validate a list of actual flavors vs a list of expected flavors.""" """Validate flavor data.
Validate a list of actual flavors vs a list of expected flavors.
"""
self.log.debug('actual: {}'.format(repr(actual))) self.log.debug('actual: {}'.format(repr(actual)))
act = [a.name for a in actual] act = [a.name for a in actual]
return self._validate_list_data(expected, act) return self._validate_list_data(expected, act)
def tenant_exists(self, keystone, tenant): def tenant_exists(self, keystone, tenant):
"""Return True if tenant exists""" """Return True if tenant exists."""
return tenant in [t.name for t in keystone.tenants.list()] return tenant in [t.name for t in keystone.tenants.list()]
def authenticate_keystone_admin(self, keystone_sentry, user, password, def authenticate_keystone_admin(self, keystone_sentry, user, password,
tenant): tenant):
"""Authenticates admin user with the keystone admin endpoint.""" """Authenticates admin user with the keystone admin endpoint."""
service_ip = \ unit = keystone_sentry
keystone_sentry.relation('shared-db', service_ip = unit.relation('shared-db',
'mysql:shared-db')['private-address'] 'mysql:shared-db')['private-address']
ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8')) ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))
return keystone_client.Client(username=user, password=password, return keystone_client.Client(username=user, password=password,
tenant_name=tenant, auth_url=ep) tenant_name=tenant, auth_url=ep)
@ -177,12 +199,40 @@ class OpenStackAmuletUtils(AmuletUtils):
image = glance.images.create(name=image_name, is_public=True, image = glance.images.create(name=image_name, is_public=True,
disk_format='qcow2', disk_format='qcow2',
container_format='bare', data=f) container_format='bare', data=f)
count = 1
status = image.status
while status != 'active' and count < 10:
time.sleep(3)
image = glance.images.get(image.id)
status = image.status
self.log.debug('image status: {}'.format(status))
count += 1
if status != 'active':
self.log.error('image creation timed out')
return None
return image return image
def delete_image(self, glance, image): def delete_image(self, glance, image):
"""Delete the specified image.""" """Delete the specified image."""
num_before = len(list(glance.images.list()))
glance.images.delete(image) glance.images.delete(image)
count = 1
num_after = len(list(glance.images.list()))
while num_after != (num_before - 1) and count < 10:
time.sleep(3)
num_after = len(list(glance.images.list()))
self.log.debug('number of images: {}'.format(num_after))
count += 1
if num_after != (num_before - 1):
self.log.error('image deletion timed out')
return False
return True
def create_instance(self, nova, image_name, instance_name, flavor): def create_instance(self, nova, image_name, instance_name, flavor):
"""Create the specified instance.""" """Create the specified instance."""
image = nova.images.find(name=image_name) image = nova.images.find(name=image_name)
@ -199,11 +249,27 @@ class OpenStackAmuletUtils(AmuletUtils):
self.log.debug('instance status: {}'.format(status)) self.log.debug('instance status: {}'.format(status))
count += 1 count += 1
if status == 'BUILD': if status != 'ACTIVE':
self.log.error('instance creation timed out')
return None return None
return instance return instance
def delete_instance(self, nova, instance): def delete_instance(self, nova, instance):
"""Delete the specified instance.""" """Delete the specified instance."""
num_before = len(list(nova.servers.list()))
nova.servers.delete(instance) nova.servers.delete(instance)
count = 1
num_after = len(list(nova.servers.list()))
while num_after != (num_before - 1) and count < 10:
time.sleep(3)
num_after = len(list(nova.servers.list()))
self.log.debug('number of instances: {}'.format(num_after))
count += 1
if num_after != (num_before - 1):
self.log.error('instance deletion timed out')
return False
return True