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
12
from charmhelpers.contrib.amulet.utils import (
20
class OpenStackAmuletUtils(AmuletUtils):
21
"""OpenStack amulet utilities.
23
This class inherits from AmuletUtils and has additional support
24
that is specifically for use by OpenStack charms.
27
def __init__(self, log_level=ERROR):
28
"""Initialize the deployment environment."""
29
super(OpenStackAmuletUtils, self).__init__(log_level)
31
def validate_endpoint_data(self, endpoints, admin_port, internal_port,
32
public_port, expected):
33
"""Validate endpoint data.
35
Validate actual endpoint data vs expected endpoint data. The ports
36
are used to find the matching endpoint.
40
self.log.debug('endpoint: {}'.format(repr(ep)))
41
if (admin_port in ep.adminurl and
42
internal_port in ep.internalurl and
43
public_port in ep.publicurl):
45
actual = {'id': ep.id,
47
'adminurl': ep.adminurl,
48
'internalurl': ep.internalurl,
49
'publicurl': ep.publicurl,
50
'service_id': ep.service_id}
51
ret = self._validate_dict_data(expected, actual)
53
return 'unexpected endpoint data - {}'.format(ret)
56
return 'endpoint not found'
58
def validate_svc_catalog_endpoint_data(self, expected, actual):
59
"""Validate service catalog endpoint data.
61
Validate a list of actual service catalog endpoints vs a list of
62
expected service catalog endpoints.
64
self.log.debug('actual: {}'.format(repr(actual)))
65
for k, v in six.iteritems(expected):
67
ret = self._validate_dict_data(expected[k][0], actual[k][0])
69
return self.endpoint_error(k, ret)
71
return "endpoint {} does not exist".format(k)
74
def validate_tenant_data(self, expected, actual):
75
"""Validate tenant data.
77
Validate a list of actual tenant data vs list of expected tenant
80
self.log.debug('actual: {}'.format(repr(actual)))
84
a = {'enabled': act.enabled, 'description': act.description,
85
'name': act.name, 'id': act.id}
86
if e['name'] == a['name']:
88
ret = self._validate_dict_data(e, a)
90
return "unexpected tenant data - {}".format(ret)
92
return "tenant {} does not exist".format(e['name'])
95
def validate_role_data(self, expected, actual):
96
"""Validate role data.
98
Validate a list of actual role data vs a list of expected role
101
self.log.debug('actual: {}'.format(repr(actual)))
105
a = {'name': act.name, 'id': act.id}
106
if e['name'] == a['name']:
108
ret = self._validate_dict_data(e, a)
110
return "unexpected role data - {}".format(ret)
112
return "role {} does not exist".format(e['name'])
115
def validate_user_data(self, expected, actual):
116
"""Validate user data.
118
Validate a list of actual user data vs a list of expected user
121
self.log.debug('actual: {}'.format(repr(actual)))
125
a = {'enabled': act.enabled, 'name': act.name,
126
'email': act.email, 'tenantId': act.tenantId,
128
if e['name'] == a['name']:
130
ret = self._validate_dict_data(e, a)
132
return "unexpected user data - {}".format(ret)
134
return "user {} does not exist".format(e['name'])
137
def validate_flavor_data(self, expected, actual):
138
"""Validate flavor data.
140
Validate a list of actual flavors vs a list of expected flavors.
142
self.log.debug('actual: {}'.format(repr(actual)))
143
act = [a.name for a in actual]
144
return self._validate_list_data(expected, act)
146
def tenant_exists(self, keystone, tenant):
147
"""Return True if tenant exists."""
148
return tenant in [t.name for t in keystone.tenants.list()]
150
def authenticate_keystone_admin(self, keystone_sentry, user, password,
152
"""Authenticates admin user with the keystone admin endpoint."""
153
unit = keystone_sentry
154
service_ip = unit.relation('shared-db',
155
'mysql:shared-db')['private-address']
156
ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))
157
return keystone_client.Client(username=user, password=password,
158
tenant_name=tenant, auth_url=ep)
160
def authenticate_keystone_user(self, keystone, user, password, tenant):
161
"""Authenticates a regular user with the keystone public endpoint."""
162
ep = keystone.service_catalog.url_for(service_type='identity',
163
endpoint_type='publicURL')
164
return keystone_client.Client(username=user, password=password,
165
tenant_name=tenant, auth_url=ep)
167
def authenticate_glance_admin(self, keystone):
168
"""Authenticates admin user with glance."""
169
ep = keystone.service_catalog.url_for(service_type='image',
170
endpoint_type='adminURL')
171
return glance_client.Client(ep, token=keystone.auth_token)
173
def authenticate_nova_user(self, keystone, user, password, tenant):
174
"""Authenticates a regular user with nova-api."""
175
ep = keystone.service_catalog.url_for(service_type='identity',
176
endpoint_type='publicURL')
177
return nova_client.Client(username=user, api_key=password,
178
project_id=tenant, auth_url=ep)
180
def create_cirros_image(self, glance, image_name):
181
"""Download the latest cirros image and upload it to glance."""
182
http_proxy = os.getenv('AMULET_HTTP_PROXY')
183
self.log.debug('AMULET_HTTP_PROXY: {}'.format(http_proxy))
185
proxies = {'http': http_proxy}
186
opener = urllib.FancyURLopener(proxies)
188
opener = urllib.FancyURLopener()
190
f = opener.open("http://download.cirros-cloud.net/version/released")
191
version = f.read().strip()
192
cirros_img = "cirros-{}-x86_64-disk.img".format(version)
193
local_path = os.path.join('tests', cirros_img)
195
if not os.path.exists(local_path):
196
cirros_url = "http://{}/{}/{}".format("download.cirros-cloud.net",
198
opener.retrieve(cirros_url, local_path)
201
with open(local_path) as f:
202
image = glance.images.create(name=image_name, is_public=True,
204
container_format='bare', data=f)
206
status = image.status
207
while status != 'active' and count < 10:
209
image = glance.images.get(image.id)
210
status = image.status
211
self.log.debug('image status: {}'.format(status))
214
if status != 'active':
215
self.log.error('image creation timed out')
220
def delete_image(self, glance, image):
221
"""Delete the specified image."""
222
num_before = len(list(glance.images.list()))
223
glance.images.delete(image)
226
num_after = len(list(glance.images.list()))
227
while num_after != (num_before - 1) and count < 10:
229
num_after = len(list(glance.images.list()))
230
self.log.debug('number of images: {}'.format(num_after))
233
if num_after != (num_before - 1):
234
self.log.error('image deletion timed out')
239
def create_instance(self, nova, image_name, instance_name, flavor):
240
"""Create the specified instance."""
241
image = nova.images.find(name=image_name)
242
flavor = nova.flavors.find(name=flavor)
243
instance = nova.servers.create(name=instance_name, image=image,
247
status = instance.status
248
while status != 'ACTIVE' and count < 60:
250
instance = nova.servers.get(instance.id)
251
status = instance.status
252
self.log.debug('instance status: {}'.format(status))
255
if status != 'ACTIVE':
256
self.log.error('instance creation timed out')
261
def delete_instance(self, nova, instance):
262
"""Delete the specified instance."""
263
num_before = len(list(nova.servers.list()))
264
nova.servers.delete(instance)
267
num_after = len(list(nova.servers.list()))
268
while num_after != (num_before - 1) and count < 10:
270
num_after = len(list(nova.servers.list()))
271
self.log.debug('number of instances: {}'.format(num_after))
274
if num_after != (num_before - 1):
275
self.log.error('instance deletion timed out')