6
import glanceclient.v1.client as glance_client
7
import keystoneclient.v2_0 as keystone_client
8
import novaclient.v1_1.client as nova_client
10
from charmhelpers.contrib.amulet.utils import (
18
class OpenStackAmuletUtils(AmuletUtils):
19
"""OpenStack amulet utilities.
21
This class inherits from AmuletUtils and has additional support
22
that is specifically for use by OpenStack charms.
25
def __init__(self, log_level=ERROR):
26
"""Initialize the deployment environment."""
27
super(OpenStackAmuletUtils, self).__init__(log_level)
29
def validate_endpoint_data(self, endpoints, admin_port, internal_port,
30
public_port, expected):
31
"""Validate endpoint data.
33
Validate actual endpoint data vs expected endpoint data. The ports
34
are used to find the matching endpoint.
38
self.log.debug('endpoint: {}'.format(repr(ep)))
39
if (admin_port in ep.adminurl and
40
internal_port in ep.internalurl and
41
public_port in ep.publicurl):
43
actual = {'id': ep.id,
45
'adminurl': ep.adminurl,
46
'internalurl': ep.internalurl,
47
'publicurl': ep.publicurl,
48
'service_id': ep.service_id}
49
ret = self._validate_dict_data(expected, actual)
51
return 'unexpected endpoint data - {}'.format(ret)
54
return 'endpoint not found'
56
def validate_svc_catalog_endpoint_data(self, expected, actual):
57
"""Validate service catalog endpoint data.
59
Validate a list of actual service catalog endpoints vs a list of
60
expected service catalog endpoints.
62
self.log.debug('actual: {}'.format(repr(actual)))
63
for k, v in expected.iteritems():
65
ret = self._validate_dict_data(expected[k][0], actual[k][0])
67
return self.endpoint_error(k, ret)
69
return "endpoint {} does not exist".format(k)
72
def validate_tenant_data(self, expected, actual):
73
"""Validate tenant data.
75
Validate a list of actual tenant data vs list of expected tenant
78
self.log.debug('actual: {}'.format(repr(actual)))
82
a = {'enabled': act.enabled, 'description': act.description,
83
'name': act.name, 'id': act.id}
84
if e['name'] == a['name']:
86
ret = self._validate_dict_data(e, a)
88
return "unexpected tenant data - {}".format(ret)
90
return "tenant {} does not exist".format(e['name'])
93
def validate_role_data(self, expected, actual):
94
"""Validate role data.
96
Validate a list of actual role data vs a list of expected role
99
self.log.debug('actual: {}'.format(repr(actual)))
103
a = {'name': act.name, 'id': act.id}
104
if e['name'] == a['name']:
106
ret = self._validate_dict_data(e, a)
108
return "unexpected role data - {}".format(ret)
110
return "role {} does not exist".format(e['name'])
113
def validate_user_data(self, expected, actual):
114
"""Validate user data.
116
Validate a list of actual user data vs a list of expected user
119
self.log.debug('actual: {}'.format(repr(actual)))
123
a = {'enabled': act.enabled, 'name': act.name,
124
'email': act.email, 'tenantId': act.tenantId,
126
if e['name'] == a['name']:
128
ret = self._validate_dict_data(e, a)
130
return "unexpected user data - {}".format(ret)
132
return "user {} does not exist".format(e['name'])
135
def validate_flavor_data(self, expected, actual):
136
"""Validate flavor data.
138
Validate a list of actual flavors vs a list of expected flavors.
140
self.log.debug('actual: {}'.format(repr(actual)))
141
act = [a.name for a in actual]
142
return self._validate_list_data(expected, act)
144
def tenant_exists(self, keystone, tenant):
145
"""Return True if tenant exists."""
146
return tenant in [t.name for t in keystone.tenants.list()]
148
def authenticate_keystone_admin(self, keystone_sentry, user, password,
150
"""Authenticates admin user with the keystone admin endpoint."""
151
unit = keystone_sentry
152
service_ip = unit.relation('shared-db',
153
'mysql:shared-db')['private-address']
154
ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))
155
return keystone_client.Client(username=user, password=password,
156
tenant_name=tenant, auth_url=ep)
158
def authenticate_keystone_user(self, keystone, user, password, tenant):
159
"""Authenticates a regular user with the keystone public endpoint."""
160
ep = keystone.service_catalog.url_for(service_type='identity',
161
endpoint_type='publicURL')
162
return keystone_client.Client(username=user, password=password,
163
tenant_name=tenant, auth_url=ep)
165
def authenticate_glance_admin(self, keystone):
166
"""Authenticates admin user with glance."""
167
ep = keystone.service_catalog.url_for(service_type='image',
168
endpoint_type='adminURL')
169
return glance_client.Client(ep, token=keystone.auth_token)
171
def authenticate_nova_user(self, keystone, user, password, tenant):
172
"""Authenticates a regular user with nova-api."""
173
ep = keystone.service_catalog.url_for(service_type='identity',
174
endpoint_type='publicURL')
175
return nova_client.Client(username=user, api_key=password,
176
project_id=tenant, auth_url=ep)
178
def create_cirros_image(self, glance, image_name):
179
"""Download the latest cirros image and upload it to glance."""
180
http_proxy = os.getenv('AMULET_HTTP_PROXY')
181
self.log.debug('AMULET_HTTP_PROXY: {}'.format(http_proxy))
183
proxies = {'http': http_proxy}
184
opener = urllib.FancyURLopener(proxies)
186
opener = urllib.FancyURLopener()
188
f = opener.open("http://download.cirros-cloud.net/version/released")
189
version = f.read().strip()
190
cirros_img = "tests/cirros-{}-x86_64-disk.img".format(version)
192
if not os.path.exists(cirros_img):
193
cirros_url = "http://{}/{}/{}".format("download.cirros-cloud.net",
195
opener.retrieve(cirros_url, cirros_img)
198
with open(cirros_img) as f:
199
image = glance.images.create(name=image_name, is_public=True,
201
container_format='bare', data=f)
203
status = image.status
204
while status != 'active' and count < 10:
206
image = glance.images.get(image.id)
207
status = image.status
208
self.log.debug('image status: {}'.format(status))
211
if status != 'active':
212
self.log.error('image creation timed out')
217
def delete_image(self, glance, image):
218
"""Delete the specified image."""
219
num_before = len(list(glance.images.list()))
220
glance.images.delete(image)
223
num_after = len(list(glance.images.list()))
224
while num_after != (num_before - 1) and count < 10:
226
num_after = len(list(glance.images.list()))
227
self.log.debug('number of images: {}'.format(num_after))
230
if num_after != (num_before - 1):
231
self.log.error('image deletion timed out')
236
def create_instance(self, nova, image_name, instance_name, flavor):
237
"""Create the specified instance."""
238
image = nova.images.find(name=image_name)
239
flavor = nova.flavors.find(name=flavor)
240
instance = nova.servers.create(name=instance_name, image=image,
244
status = instance.status
245
while status != 'ACTIVE' and count < 60:
247
instance = nova.servers.get(instance.id)
248
status = instance.status
249
self.log.debug('instance status: {}'.format(status))
252
if status != 'ACTIVE':
253
self.log.error('instance creation timed out')
258
def delete_instance(self, nova, instance):
259
"""Delete the specified instance."""
260
num_before = len(list(nova.servers.list()))
261
nova.servers.delete(instance)
264
num_after = len(list(nova.servers.list()))
265
while num_after != (num_before - 1) and count < 10:
267
num_after = len(list(nova.servers.list()))
268
self.log.debug('number of instances: {}'.format(num_after))
271
if num_after != (num_before - 1):
272
self.log.error('instance deletion timed out')