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

« back to all changes in this revision

Viewing changes to tests/basic_deployment.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
#!/usr/bin/python
 
2
 
 
3
import amulet
 
4
 
 
5
from charmhelpers.contrib.openstack.amulet.deployment import (
 
6
    OpenStackAmuletDeployment
 
7
)
 
8
 
 
9
from charmhelpers.contrib.openstack.amulet.utils import (
 
10
    OpenStackAmuletUtils,
 
11
    DEBUG, # flake8: noqa
 
12
    ERROR
 
13
)
 
14
 
 
15
# Use DEBUG to turn on debug logging
 
16
u = OpenStackAmuletUtils(ERROR)
 
17
 
 
18
 
 
19
class NovaBasicDeployment(OpenStackAmuletDeployment):
 
20
    """Amulet tests on a basic nova compute deployment."""
 
21
 
 
22
    def __init__(self, series=None, openstack=None, source=None):
 
23
        """Deploy the entire test environment."""
 
24
        super(NovaBasicDeployment, self).__init__(series, openstack, source)
 
25
        self._add_services()
 
26
        self._add_relations()
 
27
        self._configure_services()
 
28
        self._deploy()
 
29
        self._initialize_tests()
 
30
 
 
31
    def _add_services(self):
 
32
        """Add the service that we're testing, including the number of units,
 
33
           where nova-compute is local, and the other charms are from
 
34
           the charm store."""
 
35
        this_service = ('nova-compute', 1)
 
36
        other_services = [('mysql', 1), ('rabbitmq-server', 1),
 
37
                          ('nova-cloud-controller', 1), ('keystone', 1),
 
38
                          ('glance', 1)]
 
39
        super(NovaBasicDeployment, self)._add_services(this_service,
 
40
                                                       other_services)
 
41
 
 
42
    def _add_relations(self):
 
43
        """Add all of the relations for the services."""
 
44
        relations = {
 
45
          'nova-compute:image-service': 'glance:image-service',
 
46
          'nova-compute:shared-db': 'mysql:shared-db',
 
47
          'nova-compute:amqp': 'rabbitmq-server:amqp',
 
48
          'nova-cloud-controller:shared-db': 'mysql:shared-db',
 
49
          'nova-cloud-controller:identity-service': 'keystone:identity-service',
 
50
          'nova-cloud-controller:amqp': 'rabbitmq-server:amqp',
 
51
          'nova-cloud-controller:cloud-compute': 'nova-compute:cloud-compute',
 
52
          'nova-cloud-controller:image-service': 'glance:image-service',
 
53
          'keystone:shared-db': 'mysql:shared-db',
 
54
          'glance:identity-service': 'keystone:identity-service',
 
55
          'glance:shared-db': 'mysql:shared-db',
 
56
          'glance:amqp': 'rabbitmq-server:amqp'
 
57
        }
 
58
        super(NovaBasicDeployment, self)._add_relations(relations)
 
59
 
 
60
    def _configure_services(self):
 
61
        """Configure all of the services."""
 
62
        nova_config = {'config-flags': 'auto_assign_floating_ip=False',
 
63
                       'enable-live-migration': 'False'}
 
64
        keystone_config = {'admin-password': 'openstack',
 
65
                           'admin-token': 'ubuntutesting'}
 
66
        configs = {'nova-compute': nova_config, 'keystone': keystone_config}
 
67
        super(NovaBasicDeployment, self)._configure_services(configs)
 
68
 
 
69
    def _initialize_tests(self):
 
70
        """Perform final initialization before tests get run."""
 
71
        # Access the sentries for inspecting service units
 
72
        self.mysql_sentry = self.d.sentry.unit['mysql/0']
 
73
        self.keystone_sentry = self.d.sentry.unit['keystone/0']
 
74
        self.rabbitmq_sentry = self.d.sentry.unit['rabbitmq-server/0']
 
75
        self.nova_compute_sentry = self.d.sentry.unit['nova-compute/0']
 
76
        self.nova_cc_sentry = self.d.sentry.unit['nova-cloud-controller/0']
 
77
        self.glance_sentry = self.d.sentry.unit['glance/0']
 
78
 
 
79
        # Authenticate admin with keystone
 
80
        self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
 
81
                                                      user='admin',
 
82
                                                      password='openstack',
 
83
                                                      tenant='admin')
 
84
 
 
85
        # Authenticate admin with glance endpoint
 
86
        self.glance = u.authenticate_glance_admin(self.keystone)
 
87
 
 
88
        # Create a demo tenant/role/user
 
89
        self.demo_tenant = 'demoTenant'
 
90
        self.demo_role = 'demoRole'
 
91
        self.demo_user = 'demoUser'
 
92
        if not u.tenant_exists(self.keystone, self.demo_tenant):
 
93
            tenant = self.keystone.tenants.create(tenant_name=self.demo_tenant,
 
94
                                                  description='demo tenant',
 
95
                                                  enabled=True)
 
96
            self.keystone.roles.create(name=self.demo_role)
 
97
            self.keystone.users.create(name=self.demo_user,
 
98
                                       password='password',
 
99
                                       tenant_id=tenant.id,
 
100
                                       email='demo@demo.com')
 
101
 
 
102
        # Authenticate demo user with keystone
 
103
        self.keystone_demo = \
 
104
            u.authenticate_keystone_user(self.keystone, user=self.demo_user,
 
105
                                         password='password',
 
106
                                         tenant=self.demo_tenant)
 
107
 
 
108
        # Authenticate demo user with nova-api
 
109
        self.nova_demo = u.authenticate_nova_user(self.keystone,
 
110
                                                  user=self.demo_user,
 
111
                                                  password='password',
 
112
                                                  tenant=self.demo_tenant)
 
113
 
 
114
    def test_services(self):
 
115
        """Verify the expected services are running on the corresponding
 
116
           service units."""
 
117
        commands = {
 
118
            self.mysql_sentry: ['status mysql'],
 
119
            self.rabbitmq_sentry: ['sudo service rabbitmq-server status'],
 
120
            self.nova_compute_sentry: ['status nova-compute',
 
121
                                       'status nova-network',
 
122
                                       'status nova-api'],
 
123
            self.nova_cc_sentry: ['status nova-api-ec2',
 
124
                                  'status nova-api-os-compute',
 
125
                                  'status nova-objectstore',
 
126
                                  'status nova-cert',
 
127
                                  'status nova-scheduler'],
 
128
            self.keystone_sentry: ['status keystone'],
 
129
            self.glance_sentry: ['status glance-registry', 'status glance-api']
 
130
        }
 
131
        if self._get_openstack_release() >= self.precise_grizzly:
 
132
            commands[self.nova_cc_sentry] = ['status nova-conductor']
 
133
 
 
134
        ret = u.validate_services(commands)
 
135
        if ret:
 
136
            amulet.raise_status(amulet.FAIL, msg=ret)
 
137
 
 
138
    def test_service_catalog(self):
 
139
        """Verify that the service catalog endpoint data is valid."""
 
140
        endpoint_vol = {'adminURL': u.valid_url,
 
141
                        'region': 'RegionOne',
 
142
                        'publicURL': u.valid_url,
 
143
                        'internalURL': u.valid_url}
 
144
        endpoint_id = {'adminURL': u.valid_url,
 
145
                       'region': 'RegionOne',
 
146
                       'publicURL': u.valid_url,
 
147
                       'internalURL': u.valid_url}
 
148
        if self._get_openstack_release() >= self.precise_folsom:
 
149
            endpoint_vol['id'] = u.not_null
 
150
            endpoint_id['id'] = u.not_null
 
151
        expected = {'s3': [endpoint_vol], 'compute': [endpoint_vol],
 
152
                    'ec2': [endpoint_vol], 'identity': [endpoint_id]}
 
153
        actual = self.keystone_demo.service_catalog.get_endpoints()
 
154
 
 
155
        ret = u.validate_svc_catalog_endpoint_data(expected, actual)
 
156
        if ret:
 
157
            amulet.raise_status(amulet.FAIL, msg=ret)
 
158
 
 
159
    def test_openstack_compute_api_endpoint(self):
 
160
        """Verify the openstack compute api (osapi) endpoint data."""
 
161
        endpoints = self.keystone.endpoints.list()
 
162
        admin_port = internal_port = public_port = '8774'
 
163
        expected = {'id': u.not_null,
 
164
                    'region': 'RegionOne',
 
165
                    'adminurl': u.valid_url,
 
166
                    'internalurl': u.valid_url,
 
167
                    'publicurl': u.valid_url,
 
168
                    'service_id': u.not_null}
 
169
 
 
170
        ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
 
171
                                       public_port, expected)
 
172
        if ret:
 
173
            message = 'osapi endpoint: {}'.format(ret)
 
174
            amulet.raise_status(amulet.FAIL, msg=message)
 
175
 
 
176
    def test_ec2_api_endpoint(self):
 
177
        """Verify the EC2 api endpoint data."""
 
178
        endpoints = self.keystone.endpoints.list()
 
179
        admin_port = internal_port = public_port = '8773'
 
180
        expected = {'id': u.not_null,
 
181
                    'region': 'RegionOne',
 
182
                    'adminurl': u.valid_url,
 
183
                    'internalurl': u.valid_url,
 
184
                    'publicurl': u.valid_url,
 
185
                    'service_id': u.not_null}
 
186
 
 
187
        ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
 
188
                                       public_port, expected)
 
189
        if ret:
 
190
            message = 'EC2 endpoint: {}'.format(ret)
 
191
            amulet.raise_status(amulet.FAIL, msg=message)
 
192
 
 
193
    def test_s3_api_endpoint(self):
 
194
        """Verify the S3 api endpoint data."""
 
195
        endpoints = self.keystone.endpoints.list()
 
196
        admin_port = internal_port = public_port = '3333'
 
197
        expected = {'id': u.not_null,
 
198
                    'region': 'RegionOne',
 
199
                    'adminurl': u.valid_url,
 
200
                    'internalurl': u.valid_url,
 
201
                    'publicurl': u.valid_url,
 
202
                    'service_id': u.not_null}
 
203
 
 
204
        ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
 
205
                                       public_port, expected)
 
206
        if ret:
 
207
            message = 'S3 endpoint: {}'.format(ret)
 
208
            amulet.raise_status(amulet.FAIL, msg=message)
 
209
 
 
210
    def test_nova_shared_db_relation(self):
 
211
        """Verify the nova-compute to mysql shared-db relation data"""
 
212
        unit = self.nova_compute_sentry
 
213
        relation = ['shared-db', 'mysql:shared-db']
 
214
        expected = {
 
215
            'private-address': u.valid_ip,
 
216
            'nova_database': 'nova',
 
217
            'nova_username': 'nova',
 
218
            'nova_hostname': u.valid_ip
 
219
        }
 
220
 
 
221
        ret = u.validate_relation_data(unit, relation, expected)
 
222
        if ret:
 
223
            message = u.relation_error('nova-compute shared-db', ret)
 
224
            amulet.raise_status(amulet.FAIL, msg=message)
 
225
 
 
226
    def test_mysql_shared_db_relation(self):
 
227
        """Verify the mysql to nova-compute shared-db relation data"""
 
228
        unit = self.mysql_sentry
 
229
        relation = ['shared-db', 'nova-compute:shared-db']
 
230
        expected = {
 
231
            'private-address': u.valid_ip,
 
232
            'nova_password': u.not_null,
 
233
            'db_host': u.valid_ip
 
234
        }
 
235
 
 
236
        ret = u.validate_relation_data(unit, relation, expected)
 
237
        if ret:
 
238
            message = u.relation_error('mysql shared-db', ret)
 
239
            amulet.raise_status(amulet.FAIL, msg=message)
 
240
 
 
241
    def test_nova_amqp_relation(self):
 
242
        """Verify the nova-compute to rabbitmq-server amqp relation data"""
 
243
        unit = self.nova_compute_sentry
 
244
        relation = ['amqp', 'rabbitmq-server:amqp']
 
245
        expected = {
 
246
            'username': 'nova',
 
247
            'private-address': u.valid_ip,
 
248
            'vhost': 'openstack'
 
249
        }
 
250
 
 
251
        ret = u.validate_relation_data(unit, relation, expected)
 
252
        if ret:
 
253
            message = u.relation_error('nova-compute amqp', ret)
 
254
            amulet.raise_status(amulet.FAIL, msg=message)
 
255
 
 
256
    def test_rabbitmq_amqp_relation(self):
 
257
        """Verify the rabbitmq-server to nova-compute amqp relation data"""
 
258
        unit = self.rabbitmq_sentry
 
259
        relation = ['amqp', 'nova-compute:amqp']
 
260
        expected = {
 
261
            'private-address': u.valid_ip,
 
262
            'password': u.not_null,
 
263
            'hostname': u.valid_ip
 
264
        }
 
265
 
 
266
        ret = u.validate_relation_data(unit, relation, expected)
 
267
        if ret:
 
268
            message = u.relation_error('rabbitmq amqp', ret)
 
269
            amulet.raise_status(amulet.FAIL, msg=message)
 
270
 
 
271
    def test_nova_cloud_compute_relation(self):
 
272
        """Verify the nova-compute to nova-cc cloud-compute relation data"""
 
273
        unit = self.nova_compute_sentry
 
274
        relation = ['cloud-compute', 'nova-cloud-controller:cloud-compute']
 
275
        expected = {
 
276
            'private-address': u.valid_ip,
 
277
        }
 
278
 
 
279
        ret = u.validate_relation_data(unit, relation, expected)
 
280
        if ret:
 
281
            message = u.relation_error('nova-compute cloud-compute', ret)
 
282
            amulet.raise_status(amulet.FAIL, msg=message)
 
283
 
 
284
    def test_nova_cc_cloud_compute_relation(self):
 
285
        """Verify the nova-cc to nova-compute cloud-compute relation data"""
 
286
        unit = self.nova_cc_sentry
 
287
        relation = ['cloud-compute', 'nova-compute:cloud-compute']
 
288
        expected = {
 
289
            'volume_service': 'cinder',
 
290
            'network_manager': 'flatdhcpmanager',
 
291
            'ec2_host': u.valid_ip,
 
292
            'private-address': u.valid_ip,
 
293
            'restart_trigger': u.not_null
 
294
        }
 
295
        if self._get_openstack_release() == self.precise_essex:
 
296
            expected['volume_service'] = 'nova-volume'
 
297
 
 
298
        ret = u.validate_relation_data(unit, relation, expected)
 
299
        if ret:
 
300
            message = u.relation_error('nova-cc cloud-compute', ret)
 
301
            amulet.raise_status(amulet.FAIL, msg=message)
 
302
 
 
303
    def test_restart_on_config_change(self):
 
304
        """Verify that the specified services are restarted when the config
 
305
           is changed."""
 
306
        # NOTE(coreycb): Skipping failing test on essex until resolved.
 
307
        #                config-flags don't take effect on essex.
 
308
        if self._get_openstack_release() == self.precise_essex:
 
309
            u.log.error("Skipping failing test until resolved")
 
310
            return
 
311
 
 
312
        services = ['nova-compute', 'nova-api', 'nova-network']
 
313
        self.d.configure('nova-compute', {'config-flags': 'verbose=False'})
 
314
 
 
315
        time = 20
 
316
        for s in services:
 
317
            if not u.service_restarted(self.nova_compute_sentry, s,
 
318
                                       '/etc/nova/nova.conf', sleep_time=time):
 
319
                msg = "service {} didn't restart after config change".format(s)
 
320
                amulet.raise_status(amulet.FAIL, msg=msg)
 
321
            time = 0
 
322
 
 
323
        self.d.configure('nova-compute', {'config-flags': 'verbose=True'})
 
324
 
 
325
    def test_nova_config(self):
 
326
        """Verify the data in the nova config file."""
 
327
        # NOTE(coreycb): Currently no way to test on essex because config file
 
328
        #                has no section headers.
 
329
        if self._get_openstack_release() == self.precise_essex:
 
330
            return
 
331
 
 
332
        unit = self.nova_compute_sentry
 
333
        conf = '/etc/nova/nova.conf'
 
334
        rabbitmq_relation = self.rabbitmq_sentry.relation('amqp',
 
335
                                                          'nova-compute:amqp')
 
336
        glance_relation = self.glance_sentry.relation('image-service',
 
337
                                                   'nova-compute:image-service')
 
338
        mysql_relation = self.mysql_sentry.relation('shared-db',
 
339
                                                    'nova-compute:shared-db')
 
340
        db_uri = "mysql://{}:{}@{}/{}".format('nova',
 
341
                                              mysql_relation['nova_password'],
 
342
                                              mysql_relation['db_host'],
 
343
                                              'nova')
 
344
 
 
345
        expected = {'dhcpbridge_flagfile': '/etc/nova/nova.conf',
 
346
                    'dhcpbridge': '/usr/bin/nova-dhcpbridge',
 
347
                    'logdir': '/var/log/nova',
 
348
                    'state_path': '/var/lib/nova',
 
349
                    'lock_path': '/var/lock/nova',
 
350
                    'force_dhcp_release': 'True',
 
351
                    'libvirt_use_virtio_for_bridges': 'True',
 
352
                    'verbose': 'True',
 
353
                    'use_syslog': 'False',
 
354
                    'ec2_private_dns_show_ip': 'True',
 
355
                    'api_paste_config': '/etc/nova/api-paste.ini',
 
356
                    'enabled_apis': 'ec2,osapi_compute,metadata',
 
357
                    'auth_strategy': 'keystone',
 
358
                    'compute_driver': 'libvirt.LibvirtDriver',
 
359
                    'sql_connection': db_uri,
 
360
                    'rabbit_userid': 'nova',
 
361
                    'rabbit_virtual_host': 'openstack',
 
362
                    'rabbit_password': rabbitmq_relation['password'],
 
363
                    'rabbit_host': rabbitmq_relation['hostname'],
 
364
                    'glance_api_servers': glance_relation['glance-api-server'],
 
365
                    'flat_interface': 'eth1',
 
366
                    'network_manager': 'nova.network.manager.FlatDHCPManager',
 
367
                    'volume_api_class': 'nova.volume.cinder.API',
 
368
                    'verbose': 'True'}
 
369
 
 
370
        ret = u.validate_config_data(unit, conf, 'DEFAULT', expected)
 
371
        if ret:
 
372
            message = "nova config error: {}".format(ret)
 
373
            amulet.raise_status(amulet.FAIL, msg=message)
 
374
 
 
375
    def test_image_instance_create(self):
 
376
        """Create an image/instance, verify they exist, and delete them."""
 
377
        # NOTE(coreycb): Skipping failing test on essex until resolved. essex
 
378
        #                nova API calls are getting "Malformed request url (HTTP
 
379
        #                400)".
 
380
        if self._get_openstack_release() == self.precise_essex:
 
381
            u.log.error("Skipping failing test until resolved")
 
382
            return
 
383
 
 
384
        image = u.create_cirros_image(self.glance, "cirros-image")
 
385
        if not image:
 
386
            amulet.raise_status(amulet.FAIL, msg="Image create failed")
 
387
 
 
388
        instance = u.create_instance(self.nova_demo, "cirros-image", "cirros",
 
389
                                     "m1.tiny")
 
390
        if not instance:
 
391
            amulet.raise_status(amulet.FAIL, msg="Instance create failed")
 
392
 
 
393
        found = False
 
394
        for instance in self.nova_demo.servers.list():
 
395
            if instance.name == 'cirros':
 
396
                found = True
 
397
                if instance.status != 'ACTIVE':
 
398
                    msg = "cirros instance is not active"
 
399
                    amulet.raise_status(amulet.FAIL, msg=message)
 
400
 
 
401
        if not found:
 
402
            message = "nova cirros instance does not exist"
 
403
            amulet.raise_status(amulet.FAIL, msg=message)
 
404
 
 
405
        u.delete_image(self.glance, image)
 
406
        u.delete_instance(self.nova_demo, instance)