5
from charmhelpers.contrib.openstack.amulet.deployment import (
6
OpenStackAmuletDeployment
9
from charmhelpers.contrib.openstack.amulet.utils import (
14
# Use DEBUG to turn on debug logging
15
u = OpenStackAmuletUtils(DEBUG)
18
class SwiftProxyBasicDeployment(OpenStackAmuletDeployment):
19
"""Amulet tests on a basic swift-proxy deployment."""
21
def __init__(self, series, openstack=None, source=None, stable=False):
22
"""Deploy the entire test environment."""
23
super(SwiftProxyBasicDeployment, self).__init__(series, openstack,
27
self._configure_services()
29
self._initialize_tests()
31
def _add_services(self):
34
Add the services that we're testing, where swift-proxy is local,
35
and the rest of the service are from lp branches that are
36
compatible with the local charm (e.g. stable or next).
38
this_service = {'name': 'swift-proxy'}
39
other_services = [{'name': 'mysql'},
42
{'name': 'swift-storage'}]
43
super(SwiftProxyBasicDeployment, self)._add_services(this_service,
46
def _add_relations(self):
47
"""Add all of the relations for the services."""
49
'keystone:shared-db': 'mysql:shared-db',
50
'swift-proxy:identity-service': 'keystone:identity-service',
51
'swift-storage:swift-storage': 'swift-proxy:swift-storage',
52
'glance:identity-service': 'keystone:identity-service',
53
'glance:shared-db': 'mysql:shared-db',
54
'glance:object-store': 'swift-proxy:object-store'
56
super(SwiftProxyBasicDeployment, self)._add_relations(relations)
58
def _configure_services(self):
59
"""Configure all of the services."""
61
'admin-password': 'openstack',
62
'admin-token': 'ubuntutesting'
64
swift_proxy_config = {
65
'zone-assignment': 'manual',
67
'swift-hash': 'fdfef9d4-8b06-11e2-8ac0-531c923c8fae'
69
swift_storage_config = {
71
'block-device': 'vdb',
75
'keystone': keystone_config,
76
'swift-proxy': swift_proxy_config,
77
'swift-storage': swift_storage_config
79
super(SwiftProxyBasicDeployment, self)._configure_services(configs)
81
def _initialize_tests(self):
82
"""Perform final initialization before tests get run."""
83
# Access the sentries for inspecting service units
84
self.mysql_sentry = self.d.sentry.unit['mysql/0']
85
self.keystone_sentry = self.d.sentry.unit['keystone/0']
86
self.glance_sentry = self.d.sentry.unit['glance/0']
87
self.swift_proxy_sentry = self.d.sentry.unit['swift-proxy/0']
88
self.swift_storage_sentry = self.d.sentry.unit['swift-storage/0']
90
u.log.debug('openstack release val: {}'.format(
91
self._get_openstack_release()))
92
u.log.debug('openstack release str: {}'.format(
93
self._get_openstack_release_string()))
95
# Let things settle a bit before moving forward
98
# Authenticate admin with keystone
99
self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
101
password='openstack',
104
# Authenticate admin with glance endpoint
105
self.glance = u.authenticate_glance_admin(self.keystone)
107
# Authenticate swift user
108
keystone_relation = self.keystone_sentry.relation(
109
'identity-service', 'swift-proxy:identity-service')
110
ep = self.keystone.service_catalog.url_for(service_type='identity',
111
endpoint_type='publicURL')
112
self.swift = swiftclient.Connection(
114
user=keystone_relation['service_username'],
115
key=keystone_relation['service_password'],
116
tenant_name=keystone_relation['service_tenant'],
119
# Create a demo tenant/role/user
120
self.demo_tenant = 'demoTenant'
121
self.demo_role = 'demoRole'
122
self.demo_user = 'demoUser'
123
if not u.tenant_exists(self.keystone, self.demo_tenant):
124
tenant = self.keystone.tenants.create(tenant_name=self.demo_tenant,
125
description='demo tenant',
127
self.keystone.roles.create(name=self.demo_role)
128
self.keystone.users.create(name=self.demo_user,
131
email='demo@demo.com')
133
# Authenticate demo user with keystone
134
self.keystone_demo = \
135
u.authenticate_keystone_user(self.keystone, user=self.demo_user,
137
tenant=self.demo_tenant)
139
def test_100_services(self):
140
"""Verify the expected services are running on the corresponding
142
u.log.debug('Checking system services...')
143
swift_storage_services = ['swift-account',
144
'swift-account-auditor',
145
'swift-account-reaper',
146
'swift-account-replicator',
148
'swift-container-auditor',
149
'swift-container-replicator',
150
'swift-container-updater',
152
'swift-object-auditor',
153
'swift-object-replicator',
154
'swift-object-updater',
155
'swift-container-sync']
157
self.mysql_sentry: ['mysql'],
158
self.keystone_sentry: ['keystone'],
159
self.glance_sentry: ['glance-registry',
161
self.swift_proxy_sentry: ['swift-proxy'],
162
self.swift_storage_sentry: swift_storage_services
165
ret = u.validate_services_by_name(service_names)
167
amulet.raise_status(amulet.FAIL, msg=ret)
169
def test_102_users(self):
170
"""Verify all existing roles."""
171
u.log.debug('Checking keystone users...')
172
user1 = {'name': 'demoUser',
174
'tenantId': u.not_null,
176
'email': 'demo@demo.com'}
177
user2 = {'name': 'admin',
179
'tenantId': u.not_null,
181
'email': 'juju@localhost'}
182
user3 = {'name': 'glance',
184
'tenantId': u.not_null,
186
'email': u'juju@localhost'}
187
user4 = {'name': 'swift',
189
'tenantId': u.not_null,
191
'email': u'juju@localhost'}
192
expected = [user1, user2, user3, user4]
193
actual = self.keystone.users.list()
195
ret = u.validate_user_data(expected, actual)
197
amulet.raise_status(amulet.FAIL, msg=ret)
199
def test_104_keystone_service_catalog(self):
200
"""Verify that the service catalog endpoint data is valid."""
201
u.log.debug('Checking keystone service catalog...')
202
endpoint_id = {'adminURL': u.valid_url,
203
'region': 'RegionOne',
204
'publicURL': u.valid_url,
205
'internalURL': u.valid_url,
208
expected = {'image': [endpoint_id], 'object-store': [endpoint_id],
209
'identity': [endpoint_id]}
210
actual = self.keystone_demo.service_catalog.get_endpoints()
212
ret = u.validate_svc_catalog_endpoint_data(expected, actual)
214
amulet.raise_status(amulet.FAIL, msg=ret)
216
def test_106_swift_object_store_endpoint(self):
217
"""Verify the swift object-store endpoint data."""
218
u.log.debug('Checking keystone endpoint for swift object store...')
219
endpoints = self.keystone.endpoints.list()
220
admin_port = internal_port = public_port = '8080'
221
expected = {'id': u.not_null,
222
'region': 'RegionOne',
223
'adminurl': u.valid_url,
224
'internalurl': u.valid_url,
225
'publicurl': u.valid_url,
226
'service_id': u.not_null}
228
ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
229
public_port, expected)
231
message = 'object-store endpoint: {}'.format(ret)
232
amulet.raise_status(amulet.FAIL, msg=message)
234
def test_200_swift_proxy_identity_service_relation(self):
235
"""Verify the swift-proxy to keystone identity relation data."""
236
u.log.debug('Checking swift-proxy:keystone identity relation...')
237
unit = self.swift_proxy_sentry
238
relation = ['identity-service', 'keystone:identity-service']
241
'region': 'RegionOne',
242
'public_url': u.valid_url,
243
'internal_url': u.valid_url,
244
'private-address': u.valid_ip,
245
'requested_roles': 'Member,Admin',
246
'admin_url': u.valid_url
249
ret = u.validate_relation_data(unit, relation, expected)
251
message = u.relation_error('swift-proxy identity-service', ret)
252
amulet.raise_status(amulet.FAIL, msg=message)
254
def test_202_keystone_identity_service_relation(self):
255
"""Verify the keystone to swift-proxy identity relation data."""
256
u.log.debug('Checking keystone:swift-proxy identity relation...')
257
unit = self.keystone_sentry
258
relation = ['identity-service', 'swift-proxy:identity-service']
260
'service_protocol': 'http',
261
'service_tenant': 'services',
262
'admin_token': 'ubuntutesting',
263
'service_password': u.not_null,
264
'service_port': '5000',
265
'auth_port': '35357',
266
'auth_protocol': 'http',
267
'private-address': u.valid_ip,
268
'auth_host': u.valid_ip,
269
'service_username': 'swift',
270
'service_tenant_id': u.not_null,
271
'service_host': u.valid_ip
274
ret = u.validate_relation_data(unit, relation, expected)
276
message = u.relation_error('keystone identity-service', ret)
277
amulet.raise_status(amulet.FAIL, msg=message)
279
def test_204_swift_storage_swift_storage_relation(self):
280
"""Verify the swift-storage to swift-proxy swift-storage relation
282
u.log.debug('Checking swift:swift-proxy swift-storage relation...')
283
unit = self.swift_storage_sentry
284
relation = ['swift-storage', 'swift-proxy:swift-storage']
286
'account_port': '6002',
288
'object_port': '6000',
289
'container_port': '6001',
290
'private-address': u.valid_ip,
294
ret = u.validate_relation_data(unit, relation, expected)
296
message = u.relation_error('swift-storage swift-storage', ret)
297
amulet.raise_status(amulet.FAIL, msg=message)
299
def test_206_swift_proxy_swift_storage_relation(self):
300
"""Verify the swift-proxy to swift-storage swift-storage relation
302
u.log.debug('Checking swift-proxy:swift swift-storage relation...')
303
unit = self.swift_proxy_sentry
304
relation = ['swift-storage', 'swift-storage:swift-storage']
306
'private-address': u.valid_ip,
307
'trigger': u.not_null,
308
'rings_url': u.valid_url,
309
'swift_hash': u.not_null
312
ret = u.validate_relation_data(unit, relation, expected)
314
message = u.relation_error('swift-proxy swift-storage', ret)
315
amulet.raise_status(amulet.FAIL, msg=message)
317
def test_208_glance_object_store_relation(self):
318
"""Verify the glance to swift-proxy object-store relation data."""
319
u.log.debug('Checking glance:swift-proxy object-store relation...')
320
unit = self.glance_sentry
321
relation = ['object-store', 'swift-proxy:object-store']
322
expected = {'private-address': u.valid_ip}
324
ret = u.validate_relation_data(unit, relation, expected)
326
message = u.relation_error('glance object-store', ret)
327
amulet.raise_status(amulet.FAIL, msg=message)
329
def test_210_swift_proxy_object_store_relation(self):
330
"""Verify the swift-proxy to glance object-store relation data."""
331
u.log.debug('Checking swift-proxy:glance object-store relation...')
332
unit = self.swift_proxy_sentry
333
relation = ['object-store', 'glance:object-store']
334
expected = {'private-address': u.valid_ip}
335
ret = u.validate_relation_data(unit, relation, expected)
337
message = u.relation_error('swift-proxy object-store', ret)
338
amulet.raise_status(amulet.FAIL, msg=message)
340
def test_300_swift_config(self):
341
"""Verify the data in the swift-hash section of the swift config
343
u.log.debug('Checking swift config...')
344
unit = self.swift_storage_sentry
345
conf = '/etc/swift/swift.conf'
346
swift_proxy_relation = self.swift_proxy_sentry.relation(
347
'swift-storage', 'swift-storage:swift-storage')
349
'swift_hash_path_suffix': swift_proxy_relation['swift_hash']
352
ret = u.validate_config_data(unit, conf, 'swift-hash', expected)
354
message = "swift config error: {}".format(ret)
355
amulet.raise_status(amulet.FAIL, msg=message)
357
def test_302_proxy_server_config(self):
358
"""Verify the data in the proxy-server config file."""
359
u.log.debug('Checking swift proxy-server config...')
360
unit = self.swift_proxy_sentry
361
conf = '/etc/swift/proxy-server.conf'
362
keystone_relation = self.keystone_sentry.relation(
363
'identity-service', 'swift-proxy:identity-service')
364
swift_proxy_relation = unit.relation(
365
'identity-service', 'keystone:identity-service')
366
swift_proxy_ip = swift_proxy_relation['private-address']
367
auth_host = keystone_relation['auth_host']
368
auth_protocol = keystone_relation['auth_protocol']
375
'log_facility': 'LOG_LOCAL0',
377
'log_headers': 'False',
378
'log_address': '/dev/log'
381
'pipeline': 'gatekeeper healthcheck proxy-logging cache '
382
'swift3 s3token container_sync bulk tempurl '
383
'slo dlo formpost authtoken keystoneauth '
384
'staticweb container-quotas account-quotas '
385
'proxy-logging proxy-server'
387
'app:proxy-server': {
388
'use': 'egg:swift#proxy',
389
'allow_account_management': 'true',
390
'account_autocreate': 'true',
391
'node_timeout': '60',
392
'recoverable_node_timeout': '30'
395
'use': 'egg:swift#tempauth',
396
'user_system_root': 'testpass .admin https://{}:8080/v1/'
397
'AUTH_system'.format(swift_proxy_ip)
399
'filter:healthcheck': {'use': 'egg:swift#healthcheck'},
401
'use': 'egg:swift#memcache',
402
'memcache_servers': '{}:11211'.format(swift_proxy_ip)
404
'filter:account-quotas': {'use': 'egg:swift#account_quotas'},
405
'filter:container-quotas': {'use': 'egg:swift#container_quotas'},
406
'filter:proxy-logging': {'use': 'egg:swift#proxy_logging'},
407
'filter:staticweb': {'use': 'egg:swift#staticweb'},
408
'filter:bulk': {'use': 'egg:swift#bulk'},
409
'filter:slo': {'use': 'egg:swift#slo'},
410
'filter:dlo': {'use': 'egg:swift#dlo'},
411
'filter:formpost': {'use': 'egg:swift#formpost'},
412
'filter:tempurl': {'use': 'egg:swift#tempurl'},
413
'filter:container_sync': {'use': 'egg:swift#container_sync'},
414
'filter:gatekeeper': {'use': 'egg:swift#gatekeeper'},
415
'filter:keystoneauth': {
416
'use': 'egg:swift#keystoneauth',
417
'operator_roles': 'Member,Admin'
419
'filter:authtoken': {
420
'auth_uri': '{}://{}:{}'.format(
423
keystone_relation['service_port']),
424
'admin_tenant_name': keystone_relation['service_tenant'],
425
'admin_user': keystone_relation['service_username'],
426
'admin_password': keystone_relation['service_password'],
427
'delay_auth_decision': 'true',
428
'signing_dir': '/var/cache/swift',
429
'cache': 'swift.cache'
431
'filter:swift3': {'use': 'egg:swift3#swift3'}
434
if self._get_openstack_release() >= self.trusty_kilo:
436
expected['filter:authtoken'].update({
437
'paste.filter_factory': 'keystonemiddleware.auth_token:'
439
'identity_uri': '{}://{}:{}'.format(
442
keystone_relation['auth_port']),
444
expected['filter:s3token'] = {
445
# No section commonality with J and earlier
446
'paste.filter_factory': 'keystonemiddleware.s3_token'
448
'auth_uri': '{}://{}:{}'.format(
451
keystone_relation['service_port']),
452
'identity_uri': '{}://{}:{}'.format(
455
keystone_relation['auth_port']),
459
expected['filter:authtoken'].update({
460
'paste.filter_factory': 'keystoneclient.middleware.'
461
'auth_token:filter_factory',
462
'auth_host': auth_host,
463
'auth_port': keystone_relation['auth_port'],
464
'auth_protocol': auth_protocol,
466
expected['filter:s3token'] = {
467
# No section commonality with K and later
468
'paste.filter_factory': 'keystoneclient.middleware.'
469
's3_token:filter_factory',
470
'auth_port': keystone_relation['auth_port'],
471
'auth_host': keystone_relation['auth_host'],
472
'service_host': keystone_relation['service_host'],
473
'service_port': keystone_relation['service_port'],
474
'auth_protocol': keystone_relation['auth_protocol'],
475
'auth_token': keystone_relation['admin_token'],
476
'admin_token': keystone_relation['admin_token']
479
for section, pairs in expected.iteritems():
480
ret = u.validate_config_data(unit, conf, section, pairs)
482
message = "proxy-server config error: {}".format(ret)
483
amulet.raise_status(amulet.FAIL, msg=message)
485
def test_400_swift_backed_image_create(self):
486
"""Create an instance in glance, which is backed by swift, and validate
487
that some of the metadata for the image match in glance and swift."""
488
u.log.debug('Checking swift objects and containers with a '
489
'swift-backed glance image...')
491
# Create swift-backed glance image
492
img_new = u.create_cirros_image(self.glance, "cirros-image-1")
494
img_md5 = img_new.checksum
495
img_size = img_new.size
497
# Validate that swift object's checksum/size match that from glance
498
headers, containers = self.swift.get_account()
499
if len(containers) != 1:
500
msg = "Expected 1 swift container, found {}".format(
502
amulet.raise_status(amulet.FAIL, msg=msg)
504
container_name = containers[0].get('name')
506
headers, objects = self.swift.get_container(container_name)
507
if len(objects) != 1:
508
msg = "Expected 1 swift object, found {}".format(len(objects))
509
amulet.raise_status(amulet.FAIL, msg=msg)
511
swift_object_size = objects[0].get('bytes')
512
swift_object_md5 = objects[0].get('hash')
514
if img_size != swift_object_size:
515
msg = "Glance image size {} != swift object size {}".format(
516
img_size, swift_object_size)
517
amulet.raise_status(amulet.FAIL, msg=msg)
519
if img_md5 != swift_object_md5:
520
msg = "Glance image hash {} != swift object hash {}".format(
521
img_md5, swift_object_md5)
522
amulet.raise_status(amulet.FAIL, msg=msg)
525
u.delete_resource(self.glance.images, img_id, msg="glance image")
528
def test_900_restart_on_config_change(self):
529
"""Verify that the specified services are restarted when the config
531
u.log.info('Checking that conf files and system services respond '
532
'to a charm config change...')
534
sentry = self.swift_proxy_sentry
535
juju_service = 'swift-proxy'
537
# Process names, corresponding conf files
538
services = {'swift-proxy-server': '/etc/swift/proxy-server.conf'}
540
# Expected default and alternate values
541
set_default = {'node-timeout': '60'}
542
set_alternate = {'node-timeout': '90'}
544
# Make config change, check for service restarts
545
u.log.debug('Making config change on {}...'.format(juju_service))
546
mtime = u.get_sentry_time(sentry)
547
self.d.configure(juju_service, set_alternate)
550
for s, conf_file in services.iteritems():
551
u.log.debug("Checking that service restarted: {}".format(s))
552
if not u.validate_service_config_changed(sentry, mtime, s,
554
sleep_time=sleep_time):
555
self.d.configure(juju_service, set_default)
556
msg = "service {} didn't restart after config change".format(s)
557
amulet.raise_status(amulet.FAIL, msg=msg)
560
self.d.configure(juju_service, set_default)
562
def test_901_no_restart_on_config_change_when_paused(self):
563
"""Verify that the specified services are not restarted when the config
564
is changed and the unit is paused."""
565
u.log.info('Checking that system services do not get restarted '
566
'when charm config changes but unit is paused...')
567
sentry = self.swift_proxy_sentry
568
juju_service = 'swift-proxy'
570
# Expected default and alternate values
571
set_default = {'node-timeout': '60'}
572
set_alternate = {'node-timeout': '90'}
574
services = ['swift-proxy', 'haproxy', 'apache2', 'memcached']
577
u.log.debug('Pausing the unit...')
578
pause_action_id = u.run_action(sentry, "pause")
579
assert u.wait_on_action(pause_action_id), "Resume action failed."
580
# Make config change, check for service restarts
581
u.log.debug('Making config change on {}...'.format(juju_service))
582
self.d.configure(juju_service, set_alternate)
584
for service in services:
585
u.log.debug("Checking that service didn't start while "
586
"paused: {}".format(service))
587
# No explicit assert because get_process_id_list will do it for us
588
u.get_process_id_list(
589
sentry, service, expect_success=False)
591
self.d.configure(juju_service, set_default)
592
resume_action_id = u.run_action(sentry, "resume")
593
assert u.wait_on_action(resume_action_id), "Resume action failed."
595
def _assert_services(self, should_run):
596
swift_proxy_services = ['swift-proxy-server',
600
u.get_unit_process_ids(
601
{self.swift_proxy_sentry: swift_proxy_services},
602
expect_success=should_run)
603
# No point using validate_unit_process_ids, since we don't
604
# care about how many PIDs, merely that they're running, so
605
# would populate expected with either True or False. This
606
# validation is already performed in get_process_id_list
608
def _test_pause(self):
609
u.log.info("Testing pause action")
610
self._assert_services(should_run=True)
611
pause_action_id = u.run_action(self.swift_proxy_sentry, "pause")
612
assert u.wait_on_action(pause_action_id), "Pause action failed."
614
self._assert_services(should_run=False)
615
status, message = u.status_get(self.swift_proxy_sentry)
616
if status != "maintenance":
617
msg = ("Pause action failed to move unit to maintenance "
618
"status (got {} instead)".format(status))
619
amulet.raise_status(amulet.FAIL, msg=msg)
620
if message != "Paused. Use 'resume' action to resume normal service.":
621
msg = ("Pause action failed to set message"
622
" (got {} instead)".format(message))
623
amulet.raise_status(amulet.FAIL, msg=msg)
625
def _test_resume(self):
626
u.log.info("Testing resume action")
627
# service is left paused by _test_pause
628
self._assert_services(should_run=False)
629
resume_action_id = u.run_action(self.swift_proxy_sentry, "resume")
630
assert u.wait_on_action(resume_action_id), "Resume action failed."
632
self._assert_services(should_run=True)
633
status, message = u.status_get(self.swift_proxy_sentry)
634
if status != "active":
635
msg = ("Resume action failed to move unit to active "
636
"status (got {} instead)".format(status))
637
amulet.raise_status(amulet.FAIL, msg=msg)
638
if message != "Unit is ready":
639
msg = ("Resume action failed to clear message"
640
" (got {} instead)".format(message))
641
amulet.raise_status(amulet.FAIL, msg=msg)
643
def test_902_pause_resume_actions(self):
644
"""Pause and then resume swift-proxy."""
645
u.log.debug('Checking pause/resume actions...')