~james-page/charms/trusty/swift-proxy/trunk

« back to all changes in this revision

Viewing changes to tests/basic_deployment.py

  • Committer: Liam Young
  • Date: 2015-12-01 10:56:46 UTC
  • mfrom: (126.2.1 stable.remote)
  • Revision ID: liam.young@canonical.com-20151201105646-xn47a71z6lwvc3at
[brad-marshall, r=gnuoy]  Expose haproxy server and client timeouts via config.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import time
 
2
import amulet
 
3
import swiftclient
 
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
 
12
)
 
13
 
 
14
# Use DEBUG to turn on debug logging
 
15
u = OpenStackAmuletUtils(DEBUG)
 
16
 
 
17
 
 
18
class SwiftProxyBasicDeployment(OpenStackAmuletDeployment):
 
19
    """Amulet tests on a basic swift-proxy deployment."""
 
20
 
 
21
    def __init__(self, series, openstack=None, source=None, stable=False):
 
22
        """Deploy the entire test environment."""
 
23
        super(SwiftProxyBasicDeployment, self).__init__(series, openstack,
 
24
                                                        source, stable)
 
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 services
 
33
 
 
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).
 
37
           """
 
38
        this_service = {'name': 'swift-proxy'}
 
39
        other_services = [{'name': 'mysql'},
 
40
                          {'name': 'keystone'},
 
41
                          {'name': 'glance'},
 
42
                          {'name': 'swift-storage'}]
 
43
        super(SwiftProxyBasicDeployment, self)._add_services(this_service,
 
44
                                                             other_services)
 
45
 
 
46
    def _add_relations(self):
 
47
        """Add all of the relations for the services."""
 
48
        relations = {
 
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'
 
55
        }
 
56
        super(SwiftProxyBasicDeployment, self)._add_relations(relations)
 
57
 
 
58
    def _configure_services(self):
 
59
        """Configure all of the services."""
 
60
        keystone_config = {
 
61
            'admin-password': 'openstack',
 
62
            'admin-token': 'ubuntutesting'
 
63
        }
 
64
        swift_proxy_config = {
 
65
            'zone-assignment': 'manual',
 
66
            'replicas': '1',
 
67
            'swift-hash': 'fdfef9d4-8b06-11e2-8ac0-531c923c8fae'
 
68
        }
 
69
        swift_storage_config = {
 
70
            'zone': '1',
 
71
            'block-device': 'vdb',
 
72
            'overwrite': 'true'
 
73
        }
 
74
        configs = {
 
75
            'keystone': keystone_config,
 
76
            'swift-proxy': swift_proxy_config,
 
77
            'swift-storage': swift_storage_config
 
78
        }
 
79
        super(SwiftProxyBasicDeployment, self)._configure_services(configs)
 
80
 
 
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']
 
89
 
 
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()))
 
94
 
 
95
        # Let things settle a bit before moving forward
 
96
        time.sleep(30)
 
97
 
 
98
        # Authenticate admin with keystone
 
99
        self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
 
100
                                                      user='admin',
 
101
                                                      password='openstack',
 
102
                                                      tenant='admin')
 
103
 
 
104
        # Authenticate admin with glance endpoint
 
105
        self.glance = u.authenticate_glance_admin(self.keystone)
 
106
 
 
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(
 
113
            authurl=ep,
 
114
            user=keystone_relation['service_username'],
 
115
            key=keystone_relation['service_password'],
 
116
            tenant_name=keystone_relation['service_tenant'],
 
117
            auth_version='2.0')
 
118
 
 
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',
 
126
                                                  enabled=True)
 
127
            self.keystone.roles.create(name=self.demo_role)
 
128
            self.keystone.users.create(name=self.demo_user,
 
129
                                       password='password',
 
130
                                       tenant_id=tenant.id,
 
131
                                       email='demo@demo.com')
 
132
 
 
133
        # Authenticate demo user with keystone
 
134
        self.keystone_demo = \
 
135
            u.authenticate_keystone_user(self.keystone, user=self.demo_user,
 
136
                                         password='password',
 
137
                                         tenant=self.demo_tenant)
 
138
 
 
139
    def test_100_services(self):
 
140
        """Verify the expected services are running on the corresponding
 
141
           service units."""
 
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',
 
147
                                  'swift-container',
 
148
                                  'swift-container-auditor',
 
149
                                  'swift-container-replicator',
 
150
                                  'swift-container-updater',
 
151
                                  'swift-object',
 
152
                                  'swift-object-auditor',
 
153
                                  'swift-object-replicator',
 
154
                                  'swift-object-updater',
 
155
                                  'swift-container-sync']
 
156
        service_names = {
 
157
            self.mysql_sentry: ['mysql'],
 
158
            self.keystone_sentry: ['keystone'],
 
159
            self.glance_sentry: ['glance-registry',
 
160
                                 'glance-api'],
 
161
            self.swift_proxy_sentry: ['swift-proxy'],
 
162
            self.swift_storage_sentry: swift_storage_services
 
163
        }
 
164
 
 
165
        ret = u.validate_services_by_name(service_names)
 
166
        if ret:
 
167
            amulet.raise_status(amulet.FAIL, msg=ret)
 
168
 
 
169
    def test_102_users(self):
 
170
        """Verify all existing roles."""
 
171
        u.log.debug('Checking keystone users...')
 
172
        user1 = {'name': 'demoUser',
 
173
                 'enabled': True,
 
174
                 'tenantId': u.not_null,
 
175
                 'id': u.not_null,
 
176
                 'email': 'demo@demo.com'}
 
177
        user2 = {'name': 'admin',
 
178
                 'enabled': True,
 
179
                 'tenantId': u.not_null,
 
180
                 'id': u.not_null,
 
181
                 'email': 'juju@localhost'}
 
182
        user3 = {'name': 'glance',
 
183
                 'enabled': True,
 
184
                 'tenantId': u.not_null,
 
185
                 'id': u.not_null,
 
186
                 'email': u'juju@localhost'}
 
187
        user4 = {'name': 'swift',
 
188
                 'enabled': True,
 
189
                 'tenantId': u.not_null,
 
190
                 'id': u.not_null,
 
191
                 'email': u'juju@localhost'}
 
192
        expected = [user1, user2, user3, user4]
 
193
        actual = self.keystone.users.list()
 
194
 
 
195
        ret = u.validate_user_data(expected, actual)
 
196
        if ret:
 
197
            amulet.raise_status(amulet.FAIL, msg=ret)
 
198
 
 
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,
 
206
                       'id': u.not_null}
 
207
 
 
208
        expected = {'image': [endpoint_id], 'object-store': [endpoint_id],
 
209
                    'identity': [endpoint_id]}
 
210
        actual = self.keystone_demo.service_catalog.get_endpoints()
 
211
 
 
212
        ret = u.validate_svc_catalog_endpoint_data(expected, actual)
 
213
        if ret:
 
214
            amulet.raise_status(amulet.FAIL, msg=ret)
 
215
 
 
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}
 
227
 
 
228
        ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
 
229
                                       public_port, expected)
 
230
        if ret:
 
231
            message = 'object-store endpoint: {}'.format(ret)
 
232
            amulet.raise_status(amulet.FAIL, msg=message)
 
233
 
 
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']
 
239
        expected = {
 
240
            'service': 'swift',
 
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
 
247
        }
 
248
 
 
249
        ret = u.validate_relation_data(unit, relation, expected)
 
250
        if ret:
 
251
            message = u.relation_error('swift-proxy identity-service', ret)
 
252
            amulet.raise_status(amulet.FAIL, msg=message)
 
253
 
 
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']
 
259
        expected = {
 
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
 
272
        }
 
273
 
 
274
        ret = u.validate_relation_data(unit, relation, expected)
 
275
        if ret:
 
276
            message = u.relation_error('keystone identity-service', ret)
 
277
            amulet.raise_status(amulet.FAIL, msg=message)
 
278
 
 
279
    def test_204_swift_storage_swift_storage_relation(self):
 
280
        """Verify the swift-storage to swift-proxy swift-storage relation
 
281
           data."""
 
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']
 
285
        expected = {
 
286
            'account_port': '6002',
 
287
            'zone': '1',
 
288
            'object_port': '6000',
 
289
            'container_port': '6001',
 
290
            'private-address': u.valid_ip,
 
291
            'device': 'vdb'
 
292
        }
 
293
 
 
294
        ret = u.validate_relation_data(unit, relation, expected)
 
295
        if ret:
 
296
            message = u.relation_error('swift-storage swift-storage', ret)
 
297
            amulet.raise_status(amulet.FAIL, msg=message)
 
298
 
 
299
    def test_206_swift_proxy_swift_storage_relation(self):
 
300
        """Verify the swift-proxy to swift-storage swift-storage relation
 
301
           data."""
 
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']
 
305
        expected = {
 
306
            'private-address': u.valid_ip,
 
307
            'trigger': u.not_null,
 
308
            'rings_url': u.valid_url,
 
309
            'swift_hash': u.not_null
 
310
        }
 
311
 
 
312
        ret = u.validate_relation_data(unit, relation, expected)
 
313
        if ret:
 
314
            message = u.relation_error('swift-proxy swift-storage', ret)
 
315
            amulet.raise_status(amulet.FAIL, msg=message)
 
316
 
 
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}
 
323
 
 
324
        ret = u.validate_relation_data(unit, relation, expected)
 
325
        if ret:
 
326
            message = u.relation_error('glance object-store', ret)
 
327
            amulet.raise_status(amulet.FAIL, msg=message)
 
328
 
 
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)
 
336
        if ret:
 
337
            message = u.relation_error('swift-proxy object-store', ret)
 
338
            amulet.raise_status(amulet.FAIL, msg=message)
 
339
 
 
340
    def test_300_swift_config(self):
 
341
        """Verify the data in the swift-hash section of the swift config
 
342
           file."""
 
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')
 
348
        expected = {
 
349
            'swift_hash_path_suffix': swift_proxy_relation['swift_hash']
 
350
        }
 
351
 
 
352
        ret = u.validate_config_data(unit, conf, 'swift-hash', expected)
 
353
        if ret:
 
354
            message = "swift config error: {}".format(ret)
 
355
            amulet.raise_status(amulet.FAIL, msg=message)
 
356
 
 
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']
 
369
 
 
370
        expected = {
 
371
            'DEFAULT': {
 
372
                'bind_port': '8070',
 
373
                'user': 'swift',
 
374
                'log_name': 'swift',
 
375
                'log_facility': 'LOG_LOCAL0',
 
376
                'log_level': 'INFO',
 
377
                'log_headers': 'False',
 
378
                'log_address': '/dev/log'
 
379
            },
 
380
            'pipeline:main': {
 
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'
 
386
            },
 
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'
 
393
            },
 
394
            'filter:tempauth': {
 
395
                'use': 'egg:swift#tempauth',
 
396
                'user_system_root': 'testpass .admin https://{}:8080/v1/'
 
397
                                    'AUTH_system'.format(swift_proxy_ip)
 
398
            },
 
399
            'filter:healthcheck': {'use': 'egg:swift#healthcheck'},
 
400
            'filter:cache': {
 
401
                'use': 'egg:swift#memcache',
 
402
                'memcache_servers': '{}:11211'.format(swift_proxy_ip)
 
403
            },
 
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'
 
418
            },
 
419
            'filter:authtoken': {
 
420
                'auth_uri': '{}://{}:{}'.format(
 
421
                    auth_protocol,
 
422
                    auth_host,
 
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'
 
430
            },
 
431
            'filter:swift3': {'use': 'egg:swift3#swift3'}
 
432
        }
 
433
 
 
434
        if self._get_openstack_release() >= self.trusty_kilo:
 
435
            # Kilo and later
 
436
            expected['filter:authtoken'].update({
 
437
                'paste.filter_factory': 'keystonemiddleware.auth_token:'
 
438
                                        'filter_factory',
 
439
                'identity_uri': '{}://{}:{}'.format(
 
440
                    auth_protocol,
 
441
                    auth_host,
 
442
                    keystone_relation['auth_port']),
 
443
            })
 
444
            expected['filter:s3token'] = {
 
445
                # No section commonality with J and earlier
 
446
                'paste.filter_factory': 'keystonemiddleware.s3_token'
 
447
                                        ':filter_factory',
 
448
                'auth_uri': '{}://{}:{}'.format(
 
449
                    auth_protocol,
 
450
                    auth_host,
 
451
                    keystone_relation['service_port']),
 
452
                'identity_uri': '{}://{}:{}'.format(
 
453
                    auth_protocol,
 
454
                    auth_host,
 
455
                    keystone_relation['auth_port']),
 
456
            }
 
457
        else:
 
458
            # Juno and earlier
 
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,
 
465
            })
 
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']
 
477
            }
 
478
 
 
479
        for section, pairs in expected.iteritems():
 
480
            ret = u.validate_config_data(unit, conf, section, pairs)
 
481
            if ret:
 
482
                message = "proxy-server config error: {}".format(ret)
 
483
                amulet.raise_status(amulet.FAIL, msg=message)
 
484
 
 
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...')
 
490
 
 
491
        # Create swift-backed glance image
 
492
        img_new = u.create_cirros_image(self.glance, "cirros-image-1")
 
493
        img_id = img_new.id
 
494
        img_md5 = img_new.checksum
 
495
        img_size = img_new.size
 
496
 
 
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(
 
501
                len(containers))
 
502
            amulet.raise_status(amulet.FAIL, msg=msg)
 
503
 
 
504
        container_name = containers[0].get('name')
 
505
 
 
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)
 
510
 
 
511
        swift_object_size = objects[0].get('bytes')
 
512
        swift_object_md5 = objects[0].get('hash')
 
513
 
 
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)
 
518
 
 
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)
 
523
 
 
524
        # Cleanup
 
525
        u.delete_resource(self.glance.images, img_id, msg="glance image")
 
526
        u.log.info('OK')
 
527
 
 
528
    def test_900_restart_on_config_change(self):
 
529
        """Verify that the specified services are restarted when the config
 
530
           is changed."""
 
531
        u.log.info('Checking that conf files and system services respond '
 
532
                   'to a charm config change...')
 
533
 
 
534
        sentry = self.swift_proxy_sentry
 
535
        juju_service = 'swift-proxy'
 
536
 
 
537
        # Process names, corresponding conf files
 
538
        services = {'swift-proxy-server': '/etc/swift/proxy-server.conf'}
 
539
 
 
540
        # Expected default and alternate values
 
541
        set_default = {'node-timeout': '60'}
 
542
        set_alternate = {'node-timeout': '90'}
 
543
 
 
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)
 
548
 
 
549
        sleep_time = 40
 
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,
 
553
                                                     conf_file,
 
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)
 
558
            sleep_time = 0
 
559
 
 
560
        self.d.configure(juju_service, set_default)
 
561
 
 
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'
 
569
 
 
570
        # Expected default and alternate values
 
571
        set_default = {'node-timeout': '60'}
 
572
        set_alternate = {'node-timeout': '90'}
 
573
 
 
574
        services = ['swift-proxy', 'haproxy', 'apache2', 'memcached']
 
575
 
 
576
        # Pause the unit
 
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)
 
583
 
 
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)
 
590
 
 
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."
 
594
 
 
595
    def _assert_services(self, should_run):
 
596
        swift_proxy_services = ['swift-proxy-server',
 
597
                                'haproxy',
 
598
                                'apache2',
 
599
                                'memcached']
 
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
 
607
 
 
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."
 
613
 
 
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)
 
624
 
 
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."
 
631
 
 
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)
 
642
 
 
643
    def test_902_pause_resume_actions(self):
 
644
        """Pause and then resume swift-proxy."""
 
645
        u.log.debug('Checking pause/resume actions...')
 
646
        self._test_pause()
 
647
        self._test_resume()