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

Proposed by Corey Bryant
Status: Merged
Merged at revision: 74
Proposed branch: lp:~corey.bryant/charms/trusty/nova-compute/sync-charm-helpers
Merge into: lp:~openstack-charmers-archive/charms/trusty/nova-compute/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/nova-compute/sync-charm-helpers
Reviewer Review Type Date Requested Status
OpenStack Charmers Pending
Review via email: mp+228880@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
=== modified file 'hooks/charmhelpers/contrib/hahelpers/cluster.py'
--- hooks/charmhelpers/contrib/hahelpers/cluster.py 2014-07-28 14:38:51 +0000
+++ hooks/charmhelpers/contrib/hahelpers/cluster.py 2014-07-30 15:24:47 +0000
@@ -62,6 +62,15 @@
62 return peers62 return peers
6363
6464
65def peer_ips(peer_relation='cluster', addr_key='private-address'):
66 '''Return a dict of peers and their private-address'''
67 peers = {}
68 for r_id in relation_ids(peer_relation):
69 for unit in relation_list(r_id):
70 peers[unit] = relation_get(addr_key, rid=r_id, unit=unit)
71 return peers
72
73
65def oldest_peer(peers):74def oldest_peer(peers):
66 local_unit_no = int(os.getenv('JUJU_UNIT_NAME').split('/')[1])75 local_unit_no = int(os.getenv('JUJU_UNIT_NAME').split('/')[1])
67 for peer in peers:76 for peer in peers:
6877
=== modified file 'hooks/charmhelpers/contrib/openstack/amulet/deployment.py'
--- hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-28 11:36:16 +0000
+++ hooks/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-30 15:24:47 +0000
@@ -4,8 +4,11 @@
44
55
6class OpenStackAmuletDeployment(AmuletDeployment):6class OpenStackAmuletDeployment(AmuletDeployment):
7 """This class inherits from AmuletDeployment and has additional support7 """OpenStack amulet deployment.
8 that is specifically for use by OpenStack charms."""8
9 This class inherits from AmuletDeployment and has additional support
10 that is specifically for use by OpenStack charms.
11 """
912
10 def __init__(self, series=None, openstack=None, source=None):13 def __init__(self, series=None, openstack=None, source=None):
11 """Initialize the deployment environment."""14 """Initialize the deployment environment."""
@@ -40,11 +43,14 @@
40 self.d.configure(service, config)43 self.d.configure(service, config)
4144
42 def _get_openstack_release(self):45 def _get_openstack_release(self):
43 """Return an integer representing the enum value of the openstack46 """Get openstack release.
44 release."""47
45 self.precise_essex, self.precise_folsom, self.precise_grizzly, \48 Return an integer representing the enum value of the openstack
46 self.precise_havana, self.precise_icehouse, \49 release.
47 self.trusty_icehouse = range(6)50 """
51 (self.precise_essex, self.precise_folsom, self.precise_grizzly,
52 self.precise_havana, self.precise_icehouse,
53 self.trusty_icehouse) = range(6)
48 releases = {54 releases = {
49 ('precise', None): self.precise_essex,55 ('precise', None): self.precise_essex,
50 ('precise', 'cloud:precise-folsom'): self.precise_folsom,56 ('precise', 'cloud:precise-folsom'): self.precise_folsom,
5157
=== modified file 'hooks/charmhelpers/contrib/openstack/amulet/utils.py'
--- hooks/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-28 11:36:16 +0000
+++ hooks/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-30 15:24:47 +0000
@@ -16,8 +16,11 @@
1616
1717
18class OpenStackAmuletUtils(AmuletUtils):18class OpenStackAmuletUtils(AmuletUtils):
19 """This class inherits from AmuletUtils and has additional support19 """OpenStack amulet utilities.
20 that is specifically for use by OpenStack charms."""20
21 This class inherits from AmuletUtils and has additional support
22 that is specifically for use by OpenStack charms.
23 """
2124
22 def __init__(self, log_level=ERROR):25 def __init__(self, log_level=ERROR):
23 """Initialize the deployment environment."""26 """Initialize the deployment environment."""
@@ -25,13 +28,17 @@
2528
26 def validate_endpoint_data(self, endpoints, admin_port, internal_port,29 def validate_endpoint_data(self, endpoints, admin_port, internal_port,
27 public_port, expected):30 public_port, expected):
28 """Validate actual endpoint data vs expected endpoint data. The ports31 """Validate endpoint data.
29 are used to find the matching endpoint."""32
33 Validate actual endpoint data vs expected endpoint data. The ports
34 are used to find the matching endpoint.
35 """
30 found = False36 found = False
31 for ep in endpoints:37 for ep in endpoints:
32 self.log.debug('endpoint: {}'.format(repr(ep)))38 self.log.debug('endpoint: {}'.format(repr(ep)))
33 if admin_port in ep.adminurl and internal_port in ep.internalurl \39 if (admin_port in ep.adminurl and
34 and public_port in ep.publicurl:40 internal_port in ep.internalurl and
41 public_port in ep.publicurl):
35 found = True42 found = True
36 actual = {'id': ep.id,43 actual = {'id': ep.id,
37 'region': ep.region,44 'region': ep.region,
@@ -47,8 +54,11 @@
47 return 'endpoint not found'54 return 'endpoint not found'
4855
49 def validate_svc_catalog_endpoint_data(self, expected, actual):56 def validate_svc_catalog_endpoint_data(self, expected, actual):
50 """Validate a list of actual service catalog endpoints vs a list of57 """Validate service catalog endpoint data.
51 expected service catalog endpoints."""58
59 Validate a list of actual service catalog endpoints vs a list of
60 expected service catalog endpoints.
61 """
52 self.log.debug('actual: {}'.format(repr(actual)))62 self.log.debug('actual: {}'.format(repr(actual)))
53 for k, v in expected.iteritems():63 for k, v in expected.iteritems():
54 if k in actual:64 if k in actual:
@@ -60,8 +70,11 @@
60 return ret70 return ret
6171
62 def validate_tenant_data(self, expected, actual):72 def validate_tenant_data(self, expected, actual):
63 """Validate a list of actual tenant data vs list of expected tenant73 """Validate tenant data.
64 data."""74
75 Validate a list of actual tenant data vs list of expected tenant
76 data.
77 """
65 self.log.debug('actual: {}'.format(repr(actual)))78 self.log.debug('actual: {}'.format(repr(actual)))
66 for e in expected:79 for e in expected:
67 found = False80 found = False
@@ -78,8 +91,11 @@
78 return ret91 return ret
7992
80 def validate_role_data(self, expected, actual):93 def validate_role_data(self, expected, actual):
81 """Validate a list of actual role data vs a list of expected role94 """Validate role data.
82 data."""95
96 Validate a list of actual role data vs a list of expected role
97 data.
98 """
83 self.log.debug('actual: {}'.format(repr(actual)))99 self.log.debug('actual: {}'.format(repr(actual)))
84 for e in expected:100 for e in expected:
85 found = False101 found = False
@@ -95,8 +111,11 @@
95 return ret111 return ret
96112
97 def validate_user_data(self, expected, actual):113 def validate_user_data(self, expected, actual):
98 """Validate a list of actual user data vs a list of expected user114 """Validate user data.
99 data."""115
116 Validate a list of actual user data vs a list of expected user
117 data.
118 """
100 self.log.debug('actual: {}'.format(repr(actual)))119 self.log.debug('actual: {}'.format(repr(actual)))
101 for e in expected:120 for e in expected:
102 found = False121 found = False
@@ -114,21 +133,24 @@
114 return ret133 return ret
115134
116 def validate_flavor_data(self, expected, actual):135 def validate_flavor_data(self, expected, actual):
117 """Validate a list of actual flavors vs a list of expected flavors."""136 """Validate flavor data.
137
138 Validate a list of actual flavors vs a list of expected flavors.
139 """
118 self.log.debug('actual: {}'.format(repr(actual)))140 self.log.debug('actual: {}'.format(repr(actual)))
119 act = [a.name for a in actual]141 act = [a.name for a in actual]
120 return self._validate_list_data(expected, act)142 return self._validate_list_data(expected, act)
121143
122 def tenant_exists(self, keystone, tenant):144 def tenant_exists(self, keystone, tenant):
123 """Return True if tenant exists"""145 """Return True if tenant exists."""
124 return tenant in [t.name for t in keystone.tenants.list()]146 return tenant in [t.name for t in keystone.tenants.list()]
125147
126 def authenticate_keystone_admin(self, keystone_sentry, user, password,148 def authenticate_keystone_admin(self, keystone_sentry, user, password,
127 tenant):149 tenant):
128 """Authenticates admin user with the keystone admin endpoint."""150 """Authenticates admin user with the keystone admin endpoint."""
129 service_ip = \151 unit = keystone_sentry
130 keystone_sentry.relation('shared-db',152 service_ip = unit.relation('shared-db',
131 'mysql:shared-db')['private-address']153 'mysql:shared-db')['private-address']
132 ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))154 ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))
133 return keystone_client.Client(username=user, password=password,155 return keystone_client.Client(username=user, password=password,
134 tenant_name=tenant, auth_url=ep)156 tenant_name=tenant, auth_url=ep)
@@ -177,12 +199,40 @@
177 image = glance.images.create(name=image_name, is_public=True,199 image = glance.images.create(name=image_name, is_public=True,
178 disk_format='qcow2',200 disk_format='qcow2',
179 container_format='bare', data=f)201 container_format='bare', data=f)
202 count = 1
203 status = image.status
204 while status != 'active' and count < 10:
205 time.sleep(3)
206 image = glance.images.get(image.id)
207 status = image.status
208 self.log.debug('image status: {}'.format(status))
209 count += 1
210
211 if status != 'active':
212 self.log.error('image creation timed out')
213 return None
214
180 return image215 return image
181216
182 def delete_image(self, glance, image):217 def delete_image(self, glance, image):
183 """Delete the specified image."""218 """Delete the specified image."""
219 num_before = len(list(glance.images.list()))
184 glance.images.delete(image)220 glance.images.delete(image)
185221
222 count = 1
223 num_after = len(list(glance.images.list()))
224 while num_after != (num_before - 1) and count < 10:
225 time.sleep(3)
226 num_after = len(list(glance.images.list()))
227 self.log.debug('number of images: {}'.format(num_after))
228 count += 1
229
230 if num_after != (num_before - 1):
231 self.log.error('image deletion timed out')
232 return False
233
234 return True
235
186 def create_instance(self, nova, image_name, instance_name, flavor):236 def create_instance(self, nova, image_name, instance_name, flavor):
187 """Create the specified instance."""237 """Create the specified instance."""
188 image = nova.images.find(name=image_name)238 image = nova.images.find(name=image_name)
@@ -199,11 +249,27 @@
199 self.log.debug('instance status: {}'.format(status))249 self.log.debug('instance status: {}'.format(status))
200 count += 1250 count += 1
201251
202 if status == 'BUILD':252 if status != 'ACTIVE':
253 self.log.error('instance creation timed out')
203 return None254 return None
204255
205 return instance256 return instance
206257
207 def delete_instance(self, nova, instance):258 def delete_instance(self, nova, instance):
208 """Delete the specified instance."""259 """Delete the specified instance."""
260 num_before = len(list(nova.servers.list()))
209 nova.servers.delete(instance)261 nova.servers.delete(instance)
262
263 count = 1
264 num_after = len(list(nova.servers.list()))
265 while num_after != (num_before - 1) and count < 10:
266 time.sleep(3)
267 num_after = len(list(nova.servers.list()))
268 self.log.debug('number of instances: {}'.format(num_after))
269 count += 1
270
271 if num_after != (num_before - 1):
272 self.log.error('instance deletion timed out')
273 return False
274
275 return True
210276
=== modified file 'tests/charmhelpers/contrib/amulet/deployment.py'
--- tests/charmhelpers/contrib/amulet/deployment.py 2014-07-28 11:36:16 +0000
+++ tests/charmhelpers/contrib/amulet/deployment.py 2014-07-30 15:24:47 +0000
@@ -1,9 +1,14 @@
1import amulet1import amulet
22
3import os
4
35
4class AmuletDeployment(object):6class AmuletDeployment(object):
5 """This class provides generic Amulet deployment and test runner7 """Amulet deployment.
6 methods."""8
9 This class provides generic Amulet deployment and test runner
10 methods.
11 """
712
8 def __init__(self, series=None):13 def __init__(self, series=None):
9 """Initialize the deployment environment."""14 """Initialize the deployment environment."""
@@ -16,11 +21,19 @@
16 self.d = amulet.Deployment()21 self.d = amulet.Deployment()
1722
18 def _add_services(self, this_service, other_services):23 def _add_services(self, this_service, other_services):
19 """Add services to the deployment where this_service is the local charm24 """Add services.
25
26 Add services to the deployment where this_service is the local charm
20 that we're focused on testing and other_services are the other27 that we're focused on testing and other_services are the other
21 charms that come from the charm store."""28 charms that come from the charm store.
29 """
22 name, units = range(2)30 name, units = range(2)
23 self.this_service = this_service[name]31
32 if this_service[name] != os.path.basename(os.getcwd()):
33 s = this_service[name]
34 msg = "The charm's root directory name needs to be {}".format(s)
35 amulet.raise_status(amulet.FAIL, msg=msg)
36
24 self.d.add(this_service[name], units=this_service[units])37 self.d.add(this_service[name], units=this_service[units])
2538
26 for svc in other_services:39 for svc in other_services:
@@ -45,10 +58,10 @@
45 """Deploy environment and wait for all hooks to finish executing."""58 """Deploy environment and wait for all hooks to finish executing."""
46 try:59 try:
47 self.d.setup()60 self.d.setup()
48 self.d.sentry.wait()61 self.d.sentry.wait(timeout=900)
49 except amulet.helpers.TimeoutError:62 except amulet.helpers.TimeoutError:
50 amulet.raise_status(amulet.FAIL, msg="Deployment timed out")63 amulet.raise_status(amulet.FAIL, msg="Deployment timed out")
51 except:64 except Exception:
52 raise65 raise
5366
54 def run_tests(self):67 def run_tests(self):
5568
=== modified file 'tests/charmhelpers/contrib/amulet/utils.py'
--- tests/charmhelpers/contrib/amulet/utils.py 2014-07-28 11:36:16 +0000
+++ tests/charmhelpers/contrib/amulet/utils.py 2014-07-30 15:24:47 +0000
@@ -3,12 +3,15 @@
3import logging3import logging
4import re4import re
5import sys5import sys
6from time import sleep6import time
77
88
9class AmuletUtils(object):9class AmuletUtils(object):
10 """This class provides common utility functions that are used by Amulet10 """Amulet utilities.
11 tests."""11
12 This class provides common utility functions that are used by Amulet
13 tests.
14 """
1215
13 def __init__(self, log_level=logging.ERROR):16 def __init__(self, log_level=logging.ERROR):
14 self.log = self.get_logger(level=log_level)17 self.log = self.get_logger(level=log_level)
@@ -17,8 +20,8 @@
17 """Get a logger object that will log to stdout."""20 """Get a logger object that will log to stdout."""
18 log = logging21 log = logging
19 logger = log.getLogger(name)22 logger = log.getLogger(name)
20 fmt = \23 fmt = log.Formatter("%(asctime)s %(funcName)s "
21 log.Formatter("%(asctime)s %(funcName)s %(levelname)s: %(message)s")24 "%(levelname)s: %(message)s")
2225
23 handler = log.StreamHandler(stream=sys.stdout)26 handler = log.StreamHandler(stream=sys.stdout)
24 handler.setLevel(level)27 handler.setLevel(level)
@@ -38,7 +41,7 @@
38 def valid_url(self, url):41 def valid_url(self, url):
39 p = re.compile(42 p = re.compile(
40 r'^(?:http|ftp)s?://'43 r'^(?:http|ftp)s?://'
41 r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # flake8: noqa44 r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|' # noqa
42 r'localhost|'45 r'localhost|'
43 r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'46 r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'
44 r'(?::\d+)?'47 r'(?::\d+)?'
@@ -50,8 +53,11 @@
50 return False53 return False
5154
52 def validate_services(self, commands):55 def validate_services(self, commands):
53 """Verify the specified services are running on the corresponding56 """Validate services.
54 service units."""57
58 Verify the specified services are running on the corresponding
59 service units.
60 """
55 for k, v in commands.iteritems():61 for k, v in commands.iteritems():
56 for cmd in v:62 for cmd in v:
57 output, code = k.run(cmd)63 output, code = k.run(cmd)
@@ -66,9 +72,13 @@
66 config.readfp(io.StringIO(file_contents))72 config.readfp(io.StringIO(file_contents))
67 return config73 return config
6874
69 def validate_config_data(self, sentry_unit, config_file, section, expected):75 def validate_config_data(self, sentry_unit, config_file, section,
70 """Verify that the specified section of the config file contains76 expected):
71 the expected option key:value pairs."""77 """Validate config file data.
78
79 Verify that the specified section of the config file contains
80 the expected option key:value pairs.
81 """
72 config = self._get_config(sentry_unit, config_file)82 config = self._get_config(sentry_unit, config_file)
7383
74 if section != 'DEFAULT' and not config.has_section(section):84 if section != 'DEFAULT' and not config.has_section(section):
@@ -78,20 +88,23 @@
78 if not config.has_option(section, k):88 if not config.has_option(section, k):
79 return "section [{}] is missing option {}".format(section, k)89 return "section [{}] is missing option {}".format(section, k)
80 if config.get(section, k) != expected[k]:90 if config.get(section, k) != expected[k]:
81 return "section [{}] {}:{} != expected {}:{}".format(section,91 return "section [{}] {}:{} != expected {}:{}".format(
82 k, config.get(section, k), k, expected[k])92 section, k, config.get(section, k), k, expected[k])
83 return None93 return None
8494
85 def _validate_dict_data(self, expected, actual):95 def _validate_dict_data(self, expected, actual):
86 """Compare expected dictionary data vs actual dictionary data.96 """Validate dictionary data.
97
98 Compare expected dictionary data vs actual dictionary data.
87 The values in the 'expected' dictionary can be strings, bools, ints,99 The values in the 'expected' dictionary can be strings, bools, ints,
88 longs, or can be a function that evaluate a variable and returns a100 longs, or can be a function that evaluate a variable and returns a
89 bool."""101 bool.
102 """
90 for k, v in expected.iteritems():103 for k, v in expected.iteritems():
91 if k in actual:104 if k in actual:
92 if isinstance(v, basestring) or \105 if (isinstance(v, basestring) or
93 isinstance(v, bool) or \106 isinstance(v, bool) or
94 isinstance(v, (int, long)):107 isinstance(v, (int, long))):
95 if v != actual[k]:108 if v != actual[k]:
96 return "{}:{}".format(k, actual[k])109 return "{}:{}".format(k, actual[k])
97 elif not v(actual[k]):110 elif not v(actual[k]):
@@ -114,7 +127,7 @@
114 return None127 return None
115128
116 def not_null(self, string):129 def not_null(self, string):
117 if string != None:130 if string is not None:
118 return True131 return True
119 else:132 else:
120 return False133 return False
@@ -128,9 +141,12 @@
128 return sentry_unit.directory_stat(directory)['mtime']141 return sentry_unit.directory_stat(directory)['mtime']
129142
130 def _get_proc_start_time(self, sentry_unit, service, pgrep_full=False):143 def _get_proc_start_time(self, sentry_unit, service, pgrep_full=False):
131 """Determine start time of the process based on the last modification144 """Get process' start time.
145
146 Determine start time of the process based on the last modification
132 time of the /proc/pid directory. If pgrep_full is True, the process147 time of the /proc/pid directory. If pgrep_full is True, the process
133 name is matched against the full command line."""148 name is matched against the full command line.
149 """
134 if pgrep_full:150 if pgrep_full:
135 cmd = 'pgrep -o -f {}'.format(service)151 cmd = 'pgrep -o -f {}'.format(service)
136 else:152 else:
@@ -139,13 +155,16 @@
139 return self._get_dir_mtime(sentry_unit, proc_dir)155 return self._get_dir_mtime(sentry_unit, proc_dir)
140156
141 def service_restarted(self, sentry_unit, service, filename,157 def service_restarted(self, sentry_unit, service, filename,
142 pgrep_full=False):158 pgrep_full=False, sleep_time=20):
143 """Compare a service's start time vs a file's last modification time159 """Check if service was restarted.
160
161 Compare a service's start time vs a file's last modification time
144 (such as a config file for that service) to determine if the service162 (such as a config file for that service) to determine if the service
145 has been restarted."""163 has been restarted.
146 sleep(10)164 """
147 if self._get_proc_start_time(sentry_unit, service, pgrep_full) >= \165 time.sleep(sleep_time)
148 self._get_file_mtime(sentry_unit, filename):166 if (self._get_proc_start_time(sentry_unit, service, pgrep_full) >=
167 self._get_file_mtime(sentry_unit, filename)):
149 return True168 return True
150 else:169 else:
151 return False170 return False
152171
=== modified file 'tests/charmhelpers/contrib/openstack/amulet/deployment.py'
--- tests/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-28 11:36:16 +0000
+++ tests/charmhelpers/contrib/openstack/amulet/deployment.py 2014-07-30 15:24:47 +0000
@@ -4,8 +4,11 @@
44
55
6class OpenStackAmuletDeployment(AmuletDeployment):6class OpenStackAmuletDeployment(AmuletDeployment):
7 """This class inherits from AmuletDeployment and has additional support7 """OpenStack amulet deployment.
8 that is specifically for use by OpenStack charms."""8
9 This class inherits from AmuletDeployment and has additional support
10 that is specifically for use by OpenStack charms.
11 """
912
10 def __init__(self, series=None, openstack=None, source=None):13 def __init__(self, series=None, openstack=None, source=None):
11 """Initialize the deployment environment."""14 """Initialize the deployment environment."""
@@ -40,11 +43,14 @@
40 self.d.configure(service, config)43 self.d.configure(service, config)
4144
42 def _get_openstack_release(self):45 def _get_openstack_release(self):
43 """Return an integer representing the enum value of the openstack46 """Get openstack release.
44 release."""47
45 self.precise_essex, self.precise_folsom, self.precise_grizzly, \48 Return an integer representing the enum value of the openstack
46 self.precise_havana, self.precise_icehouse, \49 release.
47 self.trusty_icehouse = range(6)50 """
51 (self.precise_essex, self.precise_folsom, self.precise_grizzly,
52 self.precise_havana, self.precise_icehouse,
53 self.trusty_icehouse) = range(6)
48 releases = {54 releases = {
49 ('precise', None): self.precise_essex,55 ('precise', None): self.precise_essex,
50 ('precise', 'cloud:precise-folsom'): self.precise_folsom,56 ('precise', 'cloud:precise-folsom'): self.precise_folsom,
5157
=== modified file 'tests/charmhelpers/contrib/openstack/amulet/utils.py'
--- tests/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-28 11:36:16 +0000
+++ tests/charmhelpers/contrib/openstack/amulet/utils.py 2014-07-30 15:24:47 +0000
@@ -16,8 +16,11 @@
1616
1717
18class OpenStackAmuletUtils(AmuletUtils):18class OpenStackAmuletUtils(AmuletUtils):
19 """This class inherits from AmuletUtils and has additional support19 """OpenStack amulet utilities.
20 that is specifically for use by OpenStack charms."""20
21 This class inherits from AmuletUtils and has additional support
22 that is specifically for use by OpenStack charms.
23 """
2124
22 def __init__(self, log_level=ERROR):25 def __init__(self, log_level=ERROR):
23 """Initialize the deployment environment."""26 """Initialize the deployment environment."""
@@ -25,13 +28,17 @@
2528
26 def validate_endpoint_data(self, endpoints, admin_port, internal_port,29 def validate_endpoint_data(self, endpoints, admin_port, internal_port,
27 public_port, expected):30 public_port, expected):
28 """Validate actual endpoint data vs expected endpoint data. The ports31 """Validate endpoint data.
29 are used to find the matching endpoint."""32
33 Validate actual endpoint data vs expected endpoint data. The ports
34 are used to find the matching endpoint.
35 """
30 found = False36 found = False
31 for ep in endpoints:37 for ep in endpoints:
32 self.log.debug('endpoint: {}'.format(repr(ep)))38 self.log.debug('endpoint: {}'.format(repr(ep)))
33 if admin_port in ep.adminurl and internal_port in ep.internalurl \39 if (admin_port in ep.adminurl and
34 and public_port in ep.publicurl:40 internal_port in ep.internalurl and
41 public_port in ep.publicurl):
35 found = True42 found = True
36 actual = {'id': ep.id,43 actual = {'id': ep.id,
37 'region': ep.region,44 'region': ep.region,
@@ -47,8 +54,11 @@
47 return 'endpoint not found'54 return 'endpoint not found'
4855
49 def validate_svc_catalog_endpoint_data(self, expected, actual):56 def validate_svc_catalog_endpoint_data(self, expected, actual):
50 """Validate a list of actual service catalog endpoints vs a list of57 """Validate service catalog endpoint data.
51 expected service catalog endpoints."""58
59 Validate a list of actual service catalog endpoints vs a list of
60 expected service catalog endpoints.
61 """
52 self.log.debug('actual: {}'.format(repr(actual)))62 self.log.debug('actual: {}'.format(repr(actual)))
53 for k, v in expected.iteritems():63 for k, v in expected.iteritems():
54 if k in actual:64 if k in actual:
@@ -60,8 +70,11 @@
60 return ret70 return ret
6171
62 def validate_tenant_data(self, expected, actual):72 def validate_tenant_data(self, expected, actual):
63 """Validate a list of actual tenant data vs list of expected tenant73 """Validate tenant data.
64 data."""74
75 Validate a list of actual tenant data vs list of expected tenant
76 data.
77 """
65 self.log.debug('actual: {}'.format(repr(actual)))78 self.log.debug('actual: {}'.format(repr(actual)))
66 for e in expected:79 for e in expected:
67 found = False80 found = False
@@ -78,8 +91,11 @@
78 return ret91 return ret
7992
80 def validate_role_data(self, expected, actual):93 def validate_role_data(self, expected, actual):
81 """Validate a list of actual role data vs a list of expected role94 """Validate role data.
82 data."""95
96 Validate a list of actual role data vs a list of expected role
97 data.
98 """
83 self.log.debug('actual: {}'.format(repr(actual)))99 self.log.debug('actual: {}'.format(repr(actual)))
84 for e in expected:100 for e in expected:
85 found = False101 found = False
@@ -95,8 +111,11 @@
95 return ret111 return ret
96112
97 def validate_user_data(self, expected, actual):113 def validate_user_data(self, expected, actual):
98 """Validate a list of actual user data vs a list of expected user114 """Validate user data.
99 data."""115
116 Validate a list of actual user data vs a list of expected user
117 data.
118 """
100 self.log.debug('actual: {}'.format(repr(actual)))119 self.log.debug('actual: {}'.format(repr(actual)))
101 for e in expected:120 for e in expected:
102 found = False121 found = False
@@ -114,21 +133,24 @@
114 return ret133 return ret
115134
116 def validate_flavor_data(self, expected, actual):135 def validate_flavor_data(self, expected, actual):
117 """Validate a list of actual flavors vs a list of expected flavors."""136 """Validate flavor data.
137
138 Validate a list of actual flavors vs a list of expected flavors.
139 """
118 self.log.debug('actual: {}'.format(repr(actual)))140 self.log.debug('actual: {}'.format(repr(actual)))
119 act = [a.name for a in actual]141 act = [a.name for a in actual]
120 return self._validate_list_data(expected, act)142 return self._validate_list_data(expected, act)
121143
122 def tenant_exists(self, keystone, tenant):144 def tenant_exists(self, keystone, tenant):
123 """Return True if tenant exists"""145 """Return True if tenant exists."""
124 return tenant in [t.name for t in keystone.tenants.list()]146 return tenant in [t.name for t in keystone.tenants.list()]
125147
126 def authenticate_keystone_admin(self, keystone_sentry, user, password,148 def authenticate_keystone_admin(self, keystone_sentry, user, password,
127 tenant):149 tenant):
128 """Authenticates admin user with the keystone admin endpoint."""150 """Authenticates admin user with the keystone admin endpoint."""
129 service_ip = \151 unit = keystone_sentry
130 keystone_sentry.relation('shared-db',152 service_ip = unit.relation('shared-db',
131 'mysql:shared-db')['private-address']153 'mysql:shared-db')['private-address']
132 ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))154 ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))
133 return keystone_client.Client(username=user, password=password,155 return keystone_client.Client(username=user, password=password,
134 tenant_name=tenant, auth_url=ep)156 tenant_name=tenant, auth_url=ep)
@@ -177,12 +199,40 @@
177 image = glance.images.create(name=image_name, is_public=True,199 image = glance.images.create(name=image_name, is_public=True,
178 disk_format='qcow2',200 disk_format='qcow2',
179 container_format='bare', data=f)201 container_format='bare', data=f)
202 count = 1
203 status = image.status
204 while status != 'active' and count < 10:
205 time.sleep(3)
206 image = glance.images.get(image.id)
207 status = image.status
208 self.log.debug('image status: {}'.format(status))
209 count += 1
210
211 if status != 'active':
212 self.log.error('image creation timed out')
213 return None
214
180 return image215 return image
181216
182 def delete_image(self, glance, image):217 def delete_image(self, glance, image):
183 """Delete the specified image."""218 """Delete the specified image."""
219 num_before = len(list(glance.images.list()))
184 glance.images.delete(image)220 glance.images.delete(image)
185221
222 count = 1
223 num_after = len(list(glance.images.list()))
224 while num_after != (num_before - 1) and count < 10:
225 time.sleep(3)
226 num_after = len(list(glance.images.list()))
227 self.log.debug('number of images: {}'.format(num_after))
228 count += 1
229
230 if num_after != (num_before - 1):
231 self.log.error('image deletion timed out')
232 return False
233
234 return True
235
186 def create_instance(self, nova, image_name, instance_name, flavor):236 def create_instance(self, nova, image_name, instance_name, flavor):
187 """Create the specified instance."""237 """Create the specified instance."""
188 image = nova.images.find(name=image_name)238 image = nova.images.find(name=image_name)
@@ -199,11 +249,27 @@
199 self.log.debug('instance status: {}'.format(status))249 self.log.debug('instance status: {}'.format(status))
200 count += 1250 count += 1
201251
202 if status == 'BUILD':252 if status != 'ACTIVE':
253 self.log.error('instance creation timed out')
203 return None254 return None
204255
205 return instance256 return instance
206257
207 def delete_instance(self, nova, instance):258 def delete_instance(self, nova, instance):
208 """Delete the specified instance."""259 """Delete the specified instance."""
260 num_before = len(list(nova.servers.list()))
209 nova.servers.delete(instance)261 nova.servers.delete(instance)
262
263 count = 1
264 num_after = len(list(nova.servers.list()))
265 while num_after != (num_before - 1) and count < 10:
266 time.sleep(3)
267 num_after = len(list(nova.servers.list()))
268 self.log.debug('number of instances: {}'.format(num_after))
269 count += 1
270
271 if num_after != (num_before - 1):
272 self.log.error('instance deletion timed out')
273 return False
274
275 return True

Subscribers

People subscribed via source and target branches