~ivoks/charms/trusty/neutron-gateway/mtu-vlan

« back to all changes in this revision

Viewing changes to tests/charmhelpers/contrib/openstack/amulet/utils.py

  • Committer: Corey Bryant
  • Date: 2014-07-09 19:25:29 UTC
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: corey.bryant@canonical.com-20140709192529-c2azv58whgj6dnmd
Sync with charm-helpers

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import logging
 
2
import os
 
3
import time
 
4
import urllib
 
5
 
 
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
 
9
 
 
10
from charmhelpers.contrib.amulet.utils import (
 
11
    AmuletUtils
 
12
)
 
13
 
 
14
DEBUG = logging.DEBUG
 
15
ERROR = logging.ERROR
 
16
 
 
17
 
 
18
class OpenStackAmuletUtils(AmuletUtils):
 
19
    """This class inherits from AmuletUtils and has additional support
 
20
       that is specifically for use by OpenStack charms."""
 
21
 
 
22
    def __init__(self, log_level=ERROR):
 
23
        """Initialize the deployment environment."""
 
24
        super(OpenStackAmuletUtils, self).__init__(log_level)
 
25
 
 
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."""
 
30
        found = False
 
31
        for ep in endpoints:
 
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:
 
35
                found = True
 
36
                actual = {'id': ep.id,
 
37
                          'region': ep.region,
 
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)
 
43
                if ret:
 
44
                    return 'unexpected endpoint data - {}'.format(ret)
 
45
 
 
46
        if not found:
 
47
            return 'endpoint not found'
 
48
 
 
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():
 
54
            if k in actual:
 
55
                ret = self._validate_dict_data(expected[k][0], actual[k][0])
 
56
                if ret:
 
57
                    return self.endpoint_error(k, ret)
 
58
            else:
 
59
                return "endpoint {} does not exist".format(k)
 
60
        return ret
 
61
 
 
62
    def validate_tenant_data(self, expected, actual):
 
63
        """Validate a list of actual tenant data vs list of expected tenant
 
64
           data."""
 
65
        self.log.debug('actual: {}'.format(repr(actual)))
 
66
        for e in expected:
 
67
            found = False
 
68
            for act in actual:
 
69
                a = {'enabled': act.enabled, 'description': act.description,
 
70
                     'name': act.name, 'id': act.id}
 
71
                if e['name'] == a['name']:
 
72
                    found = True
 
73
                    ret = self._validate_dict_data(e, a)
 
74
                    if ret:
 
75
                        return "unexpected tenant data - {}".format(ret)
 
76
            if not found:
 
77
                return "tenant {} does not exist".format(e['name'])
 
78
        return ret
 
79
 
 
80
    def validate_role_data(self, expected, actual):
 
81
        """Validate a list of actual role data vs a list of expected role
 
82
           data."""
 
83
        self.log.debug('actual: {}'.format(repr(actual)))
 
84
        for e in expected:
 
85
            found = False
 
86
            for act in actual:
 
87
                a = {'name': act.name, 'id': act.id}
 
88
                if e['name'] == a['name']:
 
89
                    found = True
 
90
                    ret = self._validate_dict_data(e, a)
 
91
                    if ret:
 
92
                        return "unexpected role data - {}".format(ret)
 
93
            if not found:
 
94
                return "role {} does not exist".format(e['name'])
 
95
        return ret
 
96
 
 
97
    def validate_user_data(self, expected, actual):
 
98
        """Validate a list of actual user data vs a list of expected user
 
99
           data."""
 
100
        self.log.debug('actual: {}'.format(repr(actual)))
 
101
        for e in expected:
 
102
            found = False
 
103
            for act in actual:
 
104
                a = {'enabled': act.enabled, 'name': act.name,
 
105
                     'email': act.email, 'tenantId': act.tenantId,
 
106
                     'id': act.id}
 
107
                if e['name'] == a['name']:
 
108
                    found = True
 
109
                    ret = self._validate_dict_data(e, a)
 
110
                    if ret:
 
111
                        return "unexpected user data - {}".format(ret)
 
112
            if not found:
 
113
                return "user {} does not exist".format(e['name'])
 
114
        return ret
 
115
 
 
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)
 
121
 
 
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()]
 
125
 
 
126
    def authenticate_keystone_admin(self, keystone_sentry, user, password,
 
127
                                    tenant):
 
128
        """Authenticates admin user with the keystone admin endpoint."""
 
129
        service_ip = \
 
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)
 
135
 
 
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)
 
142
 
 
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)
 
148
 
 
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)
 
155
 
 
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))
 
160
        if http_proxy:
 
161
            proxies = {'http': http_proxy}
 
162
            opener = urllib.FancyURLopener(proxies)
 
163
        else:
 
164
            opener = urllib.FancyURLopener()
 
165
 
 
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)
 
169
 
 
170
        if not os.path.exists(cirros_img):
 
171
            cirros_url = "http://{}/{}/{}".format("download.cirros-cloud.net",
 
172
                                                  version, cirros_img)
 
173
            opener.retrieve(cirros_url, cirros_img)
 
174
        f.close()
 
175
 
 
176
        with open(cirros_img) as f:
 
177
            image = glance.images.create(name=image_name, is_public=True,
 
178
                                         disk_format='qcow2',
 
179
                                         container_format='bare', data=f)
 
180
        count = 1
 
181
        status = image.status
 
182
        while status != 'active' and count < 10:
 
183
            time.sleep(3)
 
184
            image = glance.images.get(image.id)
 
185
            status = image.status
 
186
            self.log.debug('image status: {}'.format(status))
 
187
            count += 1
 
188
 
 
189
        if status != 'active':
 
190
            self.log.error('image creation timed out')
 
191
            return None
 
192
 
 
193
        return image
 
194
 
 
195
    def delete_image(self, glance, image):
 
196
        """Delete the specified image."""
 
197
        num_before = len(list(glance.images.list()))
 
198
        glance.images.delete(image)
 
199
 
 
200
        count = 1
 
201
        num_after = len(list(glance.images.list()))
 
202
        while num_after != (num_before - 1) and count < 10:
 
203
            time.sleep(3)
 
204
            num_after = len(list(glance.images.list()))
 
205
            self.log.debug('number of images: {}'.format(num_after))
 
206
            count += 1
 
207
 
 
208
        if num_after != (num_before - 1):
 
209
            self.log.error('image deletion timed out')
 
210
            return False
 
211
 
 
212
        return True
 
213
 
 
214
    def create_instance(self, nova, image_name, instance_name, flavor):
 
215
        """Create the specified instance."""
 
216
        image = nova.images.find(name=image_name)
 
217
        flavor = nova.flavors.find(name=flavor)
 
218
        instance = nova.servers.create(name=instance_name, image=image,
 
219
                                       flavor=flavor)
 
220
 
 
221
        count = 1
 
222
        status = instance.status
 
223
        while status != 'ACTIVE' and count < 60:
 
224
            time.sleep(3)
 
225
            instance = nova.servers.get(instance.id)
 
226
            status = instance.status
 
227
            self.log.debug('instance status: {}'.format(status))
 
228
            count += 1
 
229
 
 
230
        if status != 'ACTIVE':
 
231
            self.log.error('instance creation timed out')
 
232
            return None
 
233
 
 
234
        return instance
 
235
 
 
236
    def delete_instance(self, nova, instance):
 
237
        """Delete the specified instance."""
 
238
        num_before = len(list(nova.servers.list()))
 
239
        nova.servers.delete(instance)
 
240
 
 
241
        count = 1
 
242
        num_after = len(list(nova.servers.list()))
 
243
        while num_after != (num_before - 1) and count < 10:
 
244
            time.sleep(3)
 
245
            num_after = len(list(nova.servers.list()))
 
246
            self.log.debug('number of instances: {}'.format(num_after))
 
247
            count += 1
 
248
 
 
249
        if num_after != (num_before - 1):
 
250
            self.log.error('instance deletion timed out')
 
251
            return False
 
252
 
 
253
        return True