22
19
class NovaBasicDeployment(OpenStackAmuletDeployment):
23
20
"""Amulet tests on a basic nova compute deployment."""
25
def __init__(self, series=None, openstack=None, source=None, git=False,
22
def __init__(self, series=None, openstack=None, source=None,
23
git=False, stable=False):
27
24
"""Deploy the entire test environment."""
28
super(NovaBasicDeployment, self).__init__(series, openstack, source, stable)
25
super(NovaBasicDeployment, self).__init__(series, openstack,
30
28
self._add_services()
31
29
self._add_relations()
32
30
self._configure_services()
33
u.log.info('Waiting on extended status checks...')
34
exclude_services = ['mysql']
35
self._auto_wait_for_status(exclude_services=exclude_services)
34
37
self._initialize_tests()
36
39
def _add_services(self):
41
44
compatible with the local charm (e.g. stable or next).
43
46
this_service = {'name': 'nova-compute'}
44
other_services = [{'name': 'mysql'}, {'name': 'rabbitmq-server'},
45
{'name': 'nova-cloud-controller'}, {'name': 'keystone'},
47
other_services = [{'name': 'mysql'},
48
{'name': 'rabbitmq-server'},
49
{'name': 'nova-cloud-controller'},
46
51
{'name': 'glance'}]
47
52
super(NovaBasicDeployment, self)._add_services(this_service,
50
55
def _add_relations(self):
51
56
"""Add all of the relations for the services."""
53
'nova-compute:image-service': 'glance:image-service',
54
'nova-compute:shared-db': 'mysql:shared-db',
55
'nova-compute:amqp': 'rabbitmq-server:amqp',
56
'nova-cloud-controller:shared-db': 'mysql:shared-db',
57
'nova-cloud-controller:identity-service': 'keystone:identity-service',
58
'nova-cloud-controller:amqp': 'rabbitmq-server:amqp',
59
'nova-cloud-controller:cloud-compute': 'nova-compute:cloud-compute',
60
'nova-cloud-controller:image-service': 'glance:image-service',
61
'keystone:shared-db': 'mysql:shared-db',
62
'glance:identity-service': 'keystone:identity-service',
63
'glance:shared-db': 'mysql:shared-db',
64
'glance:amqp': 'rabbitmq-server:amqp'
58
'nova-compute:image-service': 'glance:image-service',
59
'nova-compute:shared-db': 'mysql:shared-db',
60
'nova-compute:amqp': 'rabbitmq-server:amqp',
61
'nova-cloud-controller:shared-db': 'mysql:shared-db',
62
'nova-cloud-controller:identity-service': 'keystone:'
64
'nova-cloud-controller:amqp': 'rabbitmq-server:amqp',
65
'nova-cloud-controller:cloud-compute': 'nova-compute:'
67
'nova-cloud-controller:image-service': 'glance:image-service',
68
'keystone:shared-db': 'mysql:shared-db',
69
'glance:identity-service': 'keystone:identity-service',
70
'glance:shared-db': 'mysql:shared-db',
71
'glance:amqp': 'rabbitmq-server:amqp'
66
73
super(NovaBasicDeployment, self)._add_relations(relations)
99
106
'http_proxy': amulet_http_proxy,
100
107
'https_proxy': amulet_http_proxy,
102
nova_config['openstack-origin-git'] = yaml.dump(openstack_origin_git)
103
nova_cc_config['openstack-origin-git'] = yaml.dump(openstack_origin_git)
109
nova_config['openstack-origin-git'] = \
110
yaml.dump(openstack_origin_git)
112
nova_cc_config['openstack-origin-git'] = \
113
yaml.dump(openstack_origin_git)
105
115
keystone_config = {'admin-password': 'openstack',
106
116
'admin-token': 'ubuntutesting'}
118
128
self.nova_cc_sentry = self.d.sentry.unit['nova-cloud-controller/0']
119
129
self.glance_sentry = self.d.sentry.unit['glance/0']
121
# Let things settle a bit before moving forward
131
u.log.debug('openstack release val: {}'.format(
132
self._get_openstack_release()))
133
u.log.debug('openstack release str: {}'.format(
134
self._get_openstack_release_string()))
124
136
# Authenticate admin with keystone
125
137
self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
156
168
password='password',
157
169
tenant=self.demo_tenant)
159
def test_services(self):
171
def test_100_services(self):
160
172
"""Verify the expected services are running on the corresponding
161
173
service units."""
163
self.mysql_sentry: ['status mysql'],
164
self.rabbitmq_sentry: ['sudo service rabbitmq-server status'],
165
self.nova_compute_sentry: ['status nova-compute',
166
'status nova-network',
168
self.nova_cc_sentry: ['status nova-api-ec2',
169
'status nova-api-os-compute',
170
'status nova-objectstore',
172
'status nova-scheduler'],
173
self.keystone_sentry: ['status keystone'],
174
self.glance_sentry: ['status glance-registry', 'status glance-api']
174
u.log.debug('Checking system services on units...')
177
self.mysql_sentry: ['mysql'],
178
self.rabbitmq_sentry: ['rabbitmq-server'],
179
self.nova_compute_sentry: ['nova-compute',
182
self.nova_cc_sentry: ['nova-api-ec2',
183
'nova-api-os-compute',
187
self.keystone_sentry: ['keystone'],
188
self.glance_sentry: ['glance-registry',
176
192
if self._get_openstack_release() >= self.precise_grizzly:
177
commands[self.nova_cc_sentry] = ['status nova-conductor']
193
services[self.nova_cc_sentry] = ['nova-conductor']
179
ret = u.validate_services(commands)
195
ret = u.validate_services_by_name(services)
181
197
amulet.raise_status(amulet.FAIL, msg=ret)
183
def test_service_catalog(self):
199
def test_102_service_catalog(self):
184
200
"""Verify that the service catalog endpoint data is valid."""
201
u.log.debug('Checking keystone service catalog...')
185
203
endpoint_vol = {'adminURL': u.valid_url,
186
204
'region': 'RegionOne',
187
205
'publicURL': u.valid_url,
190
208
'region': 'RegionOne',
191
209
'publicURL': u.valid_url,
192
210
'internalURL': u.valid_url}
193
212
if self._get_openstack_release() >= self.precise_folsom:
194
213
endpoint_vol['id'] = u.not_null
195
214
endpoint_id['id'] = u.not_null
196
216
if self._get_openstack_release() >= self.trusty_kilo:
197
217
expected = {'compute': [endpoint_vol], 'identity': [endpoint_id]}
205
225
amulet.raise_status(amulet.FAIL, msg=ret)
207
def test_openstack_compute_api_endpoint(self):
227
def test_104_openstack_compute_api_endpoint(self):
208
228
"""Verify the openstack compute api (osapi) endpoint data."""
229
u.log.debug('Checking compute endpoint data...')
209
231
endpoints = self.keystone.endpoints.list()
210
232
admin_port = internal_port = public_port = '8774'
211
expected = {'id': u.not_null,
212
'region': 'RegionOne',
213
'adminurl': u.valid_url,
214
'internalurl': u.valid_url,
215
'publicurl': u.valid_url,
216
'service_id': u.not_null}
235
'region': 'RegionOne',
236
'adminurl': u.valid_url,
237
'internalurl': u.valid_url,
238
'publicurl': u.valid_url,
239
'service_id': u.not_null
218
242
ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
219
243
public_port, expected)
221
245
message = 'osapi endpoint: {}'.format(ret)
222
246
amulet.raise_status(amulet.FAIL, msg=message)
224
def test_ec2_api_endpoint(self):
248
def test_106_ec2_api_endpoint(self):
225
249
"""Verify the EC2 api endpoint data."""
226
250
if self._get_openstack_release() >= self.trusty_kilo:
253
u.log.debug('Checking ec2 endpoint data...')
229
254
endpoints = self.keystone.endpoints.list()
230
255
admin_port = internal_port = public_port = '8773'
231
expected = {'id': u.not_null,
232
'region': 'RegionOne',
233
'adminurl': u.valid_url,
234
'internalurl': u.valid_url,
235
'publicurl': u.valid_url,
236
'service_id': u.not_null}
258
'region': 'RegionOne',
259
'adminurl': u.valid_url,
260
'internalurl': u.valid_url,
261
'publicurl': u.valid_url,
262
'service_id': u.not_null
238
265
ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
239
266
public_port, expected)
241
268
message = 'EC2 endpoint: {}'.format(ret)
242
269
amulet.raise_status(amulet.FAIL, msg=message)
244
def test_s3_api_endpoint(self):
271
def test_108_s3_api_endpoint(self):
245
272
"""Verify the S3 api endpoint data."""
246
273
if self._get_openstack_release() >= self.trusty_kilo:
276
u.log.debug('Checking s3 endpoint data...')
249
277
endpoints = self.keystone.endpoints.list()
250
278
admin_port = internal_port = public_port = '3333'
251
expected = {'id': u.not_null,
252
'region': 'RegionOne',
253
'adminurl': u.valid_url,
254
'internalurl': u.valid_url,
255
'publicurl': u.valid_url,
256
'service_id': u.not_null}
281
'region': 'RegionOne',
282
'adminurl': u.valid_url,
283
'internalurl': u.valid_url,
284
'publicurl': u.valid_url,
285
'service_id': u.not_null
258
288
ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
259
289
public_port, expected)
261
291
message = 'S3 endpoint: {}'.format(ret)
262
292
amulet.raise_status(amulet.FAIL, msg=message)
264
def test_nova_shared_db_relation(self):
294
def test_200_nova_shared_db_relation(self):
265
295
"""Verify the nova-compute to mysql shared-db relation data"""
296
u.log.debug('Checking n-c:mysql db relation data...')
266
298
unit = self.nova_compute_sentry
267
299
relation = ['shared-db', 'mysql:shared-db']
277
309
message = u.relation_error('nova-compute shared-db', ret)
278
310
amulet.raise_status(amulet.FAIL, msg=message)
280
def test_mysql_shared_db_relation(self):
312
def test_202_mysql_shared_db_relation(self):
281
313
"""Verify the mysql to nova-compute shared-db relation data"""
314
u.log.debug('Checking mysql:n-c db relation data...')
282
315
unit = self.mysql_sentry
283
316
relation = ['shared-db', 'nova-compute:shared-db']
292
325
message = u.relation_error('mysql shared-db', ret)
293
326
amulet.raise_status(amulet.FAIL, msg=message)
295
def test_nova_amqp_relation(self):
328
def test_204_nova_amqp_relation(self):
296
329
"""Verify the nova-compute to rabbitmq-server amqp relation data"""
330
u.log.debug('Checking n-c:rmq amqp relation data...')
297
331
unit = self.nova_compute_sentry
298
332
relation = ['amqp', 'rabbitmq-server:amqp']
307
341
message = u.relation_error('nova-compute amqp', ret)
308
342
amulet.raise_status(amulet.FAIL, msg=message)
310
def test_rabbitmq_amqp_relation(self):
344
def test_206_rabbitmq_amqp_relation(self):
311
345
"""Verify the rabbitmq-server to nova-compute amqp relation data"""
346
u.log.debug('Checking rmq:n-c amqp relation data...')
312
347
unit = self.rabbitmq_sentry
313
348
relation = ['amqp', 'nova-compute:amqp']
322
357
message = u.relation_error('rabbitmq amqp', ret)
323
358
amulet.raise_status(amulet.FAIL, msg=message)
325
def test_nova_cloud_compute_relation(self):
360
def test_208_nova_cloud_compute_relation(self):
326
361
"""Verify the nova-compute to nova-cc cloud-compute relation data"""
362
u.log.debug('Checking n-c:n-c-c cloud-compute relation data...')
327
363
unit = self.nova_compute_sentry
328
364
relation = ['cloud-compute', 'nova-cloud-controller:cloud-compute']
335
371
message = u.relation_error('nova-compute cloud-compute', ret)
336
372
amulet.raise_status(amulet.FAIL, msg=message)
338
def test_nova_cc_cloud_compute_relation(self):
374
def test_210_nova_cc_cloud_compute_relation(self):
339
375
"""Verify the nova-cc to nova-compute cloud-compute relation data"""
376
u.log.debug('Checking n-c-c:n-c cloud-compute relation data...')
340
377
unit = self.nova_cc_sentry
341
378
relation = ['cloud-compute', 'nova-compute:cloud-compute']
354
391
message = u.relation_error('nova-cc cloud-compute', ret)
355
392
amulet.raise_status(amulet.FAIL, msg=message)
357
def test_z_restart_on_config_change(self):
358
"""Verify that the specified services are restarted when the config
361
Note(coreycb): The method name with the _z_ is a little odd
362
but it forces the test to run last. It just makes things
363
easier because restarting services requires re-authorization.
365
# NOTE(coreycb): Skipping failing test on essex until resolved.
366
# config-flags don't take effect on essex.
367
if self._get_openstack_release() == self.precise_essex:
368
u.log.error("Skipping failing test until resolved")
371
services = ['nova-compute', 'nova-api', 'nova-network']
372
self.d.configure('nova-compute', {'config-flags': 'verbose=False'})
376
if not u.service_restarted(self.nova_compute_sentry, s,
377
'/etc/nova/nova.conf', sleep_time=time):
378
self.d.configure('nova-compute', {'config-flags': 'verbose=True'})
379
msg = "service {} didn't restart after config change".format(s)
380
amulet.raise_status(amulet.FAIL, msg=msg)
383
self.d.configure('nova-compute', {'config-flags': 'verbose=True'})
385
def test_nova_config(self):
394
def test_300_nova_config(self):
386
395
"""Verify the data in the nova config file."""
387
396
# NOTE(coreycb): Currently no way to test on essex because config file
388
397
# has no section headers.
389
398
if self._get_openstack_release() == self.precise_essex:
401
u.log.debug('Checking nova config file data...')
392
402
unit = self.nova_compute_sentry
393
403
conf = '/etc/nova/nova.conf'
394
rabbitmq_relation = self.rabbitmq_sentry.relation('amqp',
396
glance_relation = self.glance_sentry.relation('image-service',
397
'nova-compute:image-service')
398
mysql_relation = self.mysql_sentry.relation('shared-db',
399
'nova-compute:shared-db')
404
rmq_nc_rel = self.rabbitmq_sentry.relation('amqp',
406
gl_nc_rel = self.glance_sentry.relation('image-service',
407
'nova-compute:image-service')
408
db_nc_rel = self.mysql_sentry.relation('shared-db',
409
'nova-compute:shared-db')
400
410
db_uri = "mysql://{}:{}@{}/{}".format('nova',
401
mysql_relation['nova_password'],
402
mysql_relation['db_host'],
411
db_nc_rel['nova_password'],
412
db_nc_rel['db_host'],
428
438
expected[d]['sql_connection'] = db_uri
429
439
expected[d]['rabbit_userid'] = 'nova'
430
440
expected[d]['rabbit_virtual_host'] = 'openstack'
431
expected[d]['rabbit_password'] = rabbitmq_relation['password']
432
expected[d]['rabbit_host'] = rabbitmq_relation['hostname']
433
expected[d]['glance_api_servers'] = glance_relation['glance-api-server']
441
expected[d]['rabbit_password'] = rmq_nc_rel['password']
442
expected[d]['rabbit_host'] = rmq_nc_rel['hostname']
443
expected[d]['glance_api_servers'] = gl_nc_rel['glance-api-server']
435
445
oslo_concurrency = {
436
446
'oslo_concurrency': {
446
456
'oslo_messaging_rabbit': {
447
457
'rabbit_userid': 'nova',
448
458
'rabbit_virtual_host': 'openstack',
449
'rabbit_password': rabbitmq_relation['password'],
450
'rabbit_host': rabbitmq_relation['hostname'],
459
'rabbit_password': rmq_nc_rel['password'],
460
'rabbit_host': rmq_nc_rel['hostname'],
455
'api_servers': glance_relation['glance-api-server']
465
'api_servers': gl_nc_rel['glance-api-server']
458
468
expected.update(oslo_concurrency)
466
476
message = "nova config error: {}".format(ret)
467
477
amulet.raise_status(amulet.FAIL, msg=message)
469
def test_image_instance_create(self):
479
def test_400_image_instance_create(self):
470
480
"""Create an image/instance, verify they exist, and delete them."""
471
481
# NOTE(coreycb): Skipping failing test on essex until resolved. essex
472
# nova API calls are getting "Malformed request url (HTTP
482
# nova API calls are getting "Malformed request url
474
484
if self._get_openstack_release() == self.precise_essex:
475
u.log.error("Skipping failing test until resolved")
485
u.log.error("Skipping test (due to Essex)")
488
u.log.debug('Checking nova instance creation...')
478
490
image = u.create_cirros_image(self.glance, "cirros-image")
480
492
amulet.raise_status(amulet.FAIL, msg="Image create failed")
496
508
message = "nova cirros instance does not exist"
497
509
amulet.raise_status(amulet.FAIL, msg=message)
499
u.delete_image(self.glance, image)
500
u.delete_instance(self.nova_demo, instance)
511
u.delete_resource(self.glance.images, image.id,
514
u.delete_resource(self.nova_demo.servers, instance.id,
517
def test_900_restart_on_config_change(self):
518
"""Verify that the specified services are restarted when the config
520
# NOTE(coreycb): Skipping failing test on essex until resolved.
521
# config-flags don't take effect on essex.
522
if self._get_openstack_release() == self.precise_essex:
523
u.log.error("Skipping failing test until resolved")
526
sentry = self.nova_compute_sentry
527
juju_service = 'nova-compute'
529
# Expected default and alternate values
530
set_default = {'verbose': 'False'}
531
set_alternate = {'verbose': 'True'}
533
# Services which are expected to restart upon config change,
534
# and corresponding config files affected by the change
535
conf_file = '/etc/nova/nova.conf'
537
'nova-compute': conf_file,
538
'nova-api': conf_file,
539
'nova-network': conf_file
542
# Make config change, check for service restarts
543
u.log.debug('Making config change on {}...'.format(juju_service))
544
mtime = u.get_sentry_time(sentry)
545
self.d.configure(juju_service, set_alternate)
548
for s, conf_file in services.iteritems():
549
u.log.debug("Checking that service restarted: {}".format(s))
550
if not u.validate_service_config_changed(sentry, mtime, s,
552
sleep_time=sleep_time):
554
self.d.configure(juju_service, set_default)
555
msg = "service {} didn't restart after config change".format(s)
556
amulet.raise_status(amulet.FAIL, msg=msg)
559
self.d.configure(juju_service, set_default)