~gnuoy/charms/trusty/nova-compute/resurrect-peer-relation

« back to all changes in this revision

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

  • Committer: james.page at ubuntu
  • Date: 2014-07-28 11:36:16 UTC
  • mfrom: (68.1.3 nova-compute)
  • Revision ID: james.page@ubuntu.com-20140728113616-spl81505m0q840sy
[corey.bryant,r=james-page] Add amulet tests

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
        return image
 
181
 
 
182
    def delete_image(self, glance, image):
 
183
        """Delete the specified image."""
 
184
        glance.images.delete(image)
 
185
 
 
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,
 
191
                                       flavor=flavor)
 
192
 
 
193
        count = 1
 
194
        status = instance.status
 
195
        while status != 'ACTIVE' and count < 60:
 
196
            time.sleep(3)
 
197
            instance = nova.servers.get(instance.id)
 
198
            status = instance.status
 
199
            self.log.debug('instance status: {}'.format(status))
 
200
            count += 1
 
201
 
 
202
        if status == 'BUILD':
 
203
            return None
 
204
 
 
205
        return instance
 
206
 
 
207
    def delete_instance(self, nova, instance):
 
208
        """Delete the specified instance."""
 
209
        nova.servers.delete(instance)