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
"""This class inherits from AmuletUtils and has additional support
20
that is specifically for use by OpenStack charms."""
22
def __init__(self, log_level=ERROR):
23
"""Initialize the deployment environment."""
24
super(OpenStackAmuletUtils, self).__init__(log_level)
26
def validate_endpoint_data(self, endpoints, admin_port, internal_port,
27
public_port, expected):
28
"""Validate actual endpoint data vs expected endpoint data. The ports
29
are used to find the matching endpoint."""
32
self.log.debug('endpoint: {}'.format(repr(ep)))
33
if admin_port in ep.adminurl and internal_port in ep.internalurl \
34
and public_port in ep.publicurl:
36
actual = {'id': ep.id,
38
'adminurl': ep.adminurl,
39
'internalurl': ep.internalurl,
40
'publicurl': ep.publicurl,
41
'service_id': ep.service_id}
42
ret = self._validate_dict_data(expected, actual)
44
return 'unexpected endpoint data - {}'.format(ret)
47
return 'endpoint not found'
49
def validate_svc_catalog_endpoint_data(self, expected, actual):
50
"""Validate a list of actual service catalog endpoints vs a list of
51
expected service catalog endpoints."""
52
self.log.debug('actual: {}'.format(repr(actual)))
53
for k, v in expected.iteritems():
55
ret = self._validate_dict_data(expected[k][0], actual[k][0])
57
return self.endpoint_error(k, ret)
59
return "endpoint {} does not exist".format(k)
62
def validate_tenant_data(self, expected, actual):
63
"""Validate a list of actual tenant data vs list of expected tenant
65
self.log.debug('actual: {}'.format(repr(actual)))
69
a = {'enabled': act.enabled, 'description': act.description,
70
'name': act.name, 'id': act.id}
71
if e['name'] == a['name']:
73
ret = self._validate_dict_data(e, a)
75
return "unexpected tenant data - {}".format(ret)
77
return "tenant {} does not exist".format(e['name'])
80
def validate_role_data(self, expected, actual):
81
"""Validate a list of actual role data vs a list of expected role
83
self.log.debug('actual: {}'.format(repr(actual)))
87
a = {'name': act.name, 'id': act.id}
88
if e['name'] == a['name']:
90
ret = self._validate_dict_data(e, a)
92
return "unexpected role data - {}".format(ret)
94
return "role {} does not exist".format(e['name'])
97
def validate_user_data(self, expected, actual):
98
"""Validate a list of actual user data vs a list of expected user
100
self.log.debug('actual: {}'.format(repr(actual)))
104
a = {'enabled': act.enabled, 'name': act.name,
105
'email': act.email, 'tenantId': act.tenantId,
107
if e['name'] == a['name']:
109
ret = self._validate_dict_data(e, a)
111
return "unexpected user data - {}".format(ret)
113
return "user {} does not exist".format(e['name'])
116
def validate_flavor_data(self, expected, actual):
117
"""Validate a list of actual flavors vs a list of expected flavors."""
118
self.log.debug('actual: {}'.format(repr(actual)))
119
act = [a.name for a in actual]
120
return self._validate_list_data(expected, act)
122
def tenant_exists(self, keystone, tenant):
123
"""Return True if tenant exists"""
124
return tenant in [t.name for t in keystone.tenants.list()]
126
def authenticate_keystone_admin(self, keystone_sentry, user, password,
128
"""Authenticates admin user with the keystone admin endpoint."""
130
keystone_sentry.relation('shared-db',
131
'mysql:shared-db')['private-address']
132
ep = "http://{}:35357/v2.0".format(service_ip.strip().decode('utf-8'))
133
return keystone_client.Client(username=user, password=password,
134
tenant_name=tenant, auth_url=ep)
136
def authenticate_keystone_user(self, keystone, user, password, tenant):
137
"""Authenticates a regular user with the keystone public endpoint."""
138
ep = keystone.service_catalog.url_for(service_type='identity',
139
endpoint_type='publicURL')
140
return keystone_client.Client(username=user, password=password,
141
tenant_name=tenant, auth_url=ep)
143
def authenticate_glance_admin(self, keystone):
144
"""Authenticates admin user with glance."""
145
ep = keystone.service_catalog.url_for(service_type='image',
146
endpoint_type='adminURL')
147
return glance_client.Client(ep, token=keystone.auth_token)
149
def authenticate_nova_user(self, keystone, user, password, tenant):
150
"""Authenticates a regular user with nova-api."""
151
ep = keystone.service_catalog.url_for(service_type='identity',
152
endpoint_type='publicURL')
153
return nova_client.Client(username=user, api_key=password,
154
project_id=tenant, auth_url=ep)
156
def create_cirros_image(self, glance, image_name):
157
"""Download the latest cirros image and upload it to glance."""
158
http_proxy = os.getenv('AMULET_HTTP_PROXY')
159
self.log.debug('AMULET_HTTP_PROXY: {}'.format(http_proxy))
161
proxies = {'http': http_proxy}
162
opener = urllib.FancyURLopener(proxies)
164
opener = urllib.FancyURLopener()
166
f = opener.open("http://download.cirros-cloud.net/version/released")
167
version = f.read().strip()
168
cirros_img = "tests/cirros-{}-x86_64-disk.img".format(version)
170
if not os.path.exists(cirros_img):
171
cirros_url = "http://{}/{}/{}".format("download.cirros-cloud.net",
173
opener.retrieve(cirros_url, cirros_img)
176
with open(cirros_img) as f:
177
image = glance.images.create(name=image_name, is_public=True,
179
container_format='bare', data=f)
182
def delete_image(self, glance, image):
183
"""Delete the specified image."""
184
glance.images.delete(image)
186
def create_instance(self, nova, image_name, instance_name, flavor):
187
"""Create the specified instance."""
188
image = nova.images.find(name=image_name)
189
flavor = nova.flavors.find(name=flavor)
190
instance = nova.servers.create(name=instance_name, image=image,
194
status = instance.status
195
while status != 'ACTIVE' and count < 60:
197
instance = nova.servers.get(instance.id)
198
status = instance.status
199
self.log.debug('instance status: {}'.format(status))
202
if status == 'BUILD':
207
def delete_instance(self, nova, instance):
208
"""Delete the specified instance."""
209
nova.servers.delete(instance)