~bjornt/charms/trusty/swift-storage/forward-port-bug-1350049-fix

« back to all changes in this revision

Viewing changes to tests/basic_deployment.py

  • Committer: james.page at ubuntu
  • Date: 2014-07-28 11:54:32 UTC
  • mfrom: (32.1.3 swift-storage)
  • Revision ID: james.page@ubuntu.com-20140728115432-em4ntc388qe058e3
[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
import swiftclient
 
5
 
 
6
from charmhelpers.contrib.openstack.amulet.deployment import (
 
7
    OpenStackAmuletDeployment
 
8
)
 
9
 
 
10
from charmhelpers.contrib.openstack.amulet.utils import (
 
11
    OpenStackAmuletUtils,
 
12
    DEBUG, # flake8: noqa
 
13
    ERROR
 
14
)
 
15
 
 
16
# Use DEBUG to turn on debug logging
 
17
u = OpenStackAmuletUtils(ERROR)
 
18
 
 
19
 
 
20
class SwiftStorageBasicDeployment(OpenStackAmuletDeployment):
 
21
    """Amulet tests on a basic swift-storage deployment."""
 
22
 
 
23
    def __init__(self, series, openstack=None, source=None):
 
24
        """Deploy the entire test environment."""
 
25
        super(SwiftStorageBasicDeployment, self).__init__(series, openstack,
 
26
                                                          source)
 
27
        self._add_services()
 
28
        self._add_relations()
 
29
        self._configure_services()
 
30
        self._deploy()
 
31
        self._initialize_tests()
 
32
 
 
33
    def _add_services(self):
 
34
        """Add the service that we're testing, including the number of units,
 
35
           where swift-storage is local, and the other charms are from
 
36
           the charm store."""
 
37
        this_service = ('swift-storage', 1)
 
38
        other_services = [('mysql', 1),
 
39
                          ('keystone', 1), ('glance', 1), ('swift-proxy', 1)]
 
40
        super(SwiftStorageBasicDeployment, self)._add_services(this_service,
 
41
                                                               other_services)
 
42
 
 
43
    def _add_relations(self):
 
44
        """Add all of the relations for the services."""
 
45
        relations = {
 
46
          'keystone:shared-db': 'mysql:shared-db',
 
47
          'swift-proxy:identity-service': 'keystone:identity-service',
 
48
          'swift-storage:swift-storage': 'swift-proxy:swift-storage',
 
49
          'glance:identity-service': 'keystone:identity-service',
 
50
          'glance:shared-db': 'mysql:shared-db',
 
51
          'glance:object-store': 'swift-proxy:object-store'
 
52
        }
 
53
        super(SwiftStorageBasicDeployment, self)._add_relations(relations)
 
54
 
 
55
    def _configure_services(self):
 
56
        """Configure all of the services."""
 
57
        keystone_config = {'admin-password': 'openstack',
 
58
                           'admin-token': 'ubuntutesting'}
 
59
        swift_proxy_config = {'zone-assignment': 'manual',
 
60
                           'replicas': '1',
 
61
                           'swift-hash': 'fdfef9d4-8b06-11e2-8ac0-531c923c8fae',
 
62
                           'use-https': 'no'}
 
63
        swift_storage_config = {'zone': '1',
 
64
                                'block-device': 'vdb',
 
65
                                'overwrite': 'true'}
 
66
        configs = {'keystone': keystone_config,
 
67
                   'swift-proxy': swift_proxy_config,
 
68
                   'swift-storage': swift_storage_config}
 
69
        super(SwiftStorageBasicDeployment, self)._configure_services(configs)
 
70
 
 
71
    def _initialize_tests(self):
 
72
        """Perform final initialization before tests get run."""
 
73
        # Access the sentries for inspecting service units
 
74
        self.mysql_sentry = self.d.sentry.unit['mysql/0']
 
75
        self.keystone_sentry = self.d.sentry.unit['keystone/0']
 
76
        self.glance_sentry = self.d.sentry.unit['glance/0']
 
77
        self.swift_proxy_sentry = self.d.sentry.unit['swift-proxy/0']
 
78
        self.swift_storage_sentry = self.d.sentry.unit['swift-storage/0']
 
79
 
 
80
        # Authenticate admin with keystone
 
81
        self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
 
82
                                                      user='admin',
 
83
                                                      password='openstack',
 
84
                                                      tenant='admin')
 
85
 
 
86
        # Authenticate admin with glance endpoint
 
87
        self.glance = u.authenticate_glance_admin(self.keystone)
 
88
 
 
89
        # Authenticate swift user
 
90
        keystone_relation = self.keystone_sentry.relation('identity-service',
 
91
                                                 'swift-proxy:identity-service')
 
92
        ep = self.keystone.service_catalog.url_for(service_type='identity',
 
93
                                                   endpoint_type='publicURL')
 
94
        self.swift = swiftclient.Connection(authurl=ep,
 
95
                                user=keystone_relation['service_username'],
 
96
                                key=keystone_relation['service_password'],
 
97
                                tenant_name=keystone_relation['service_tenant'],
 
98
                                auth_version='2.0')
 
99
 
 
100
        # Create a demo tenant/role/user
 
101
        self.demo_tenant = 'demoTenant'
 
102
        self.demo_role = 'demoRole'
 
103
        self.demo_user = 'demoUser'
 
104
        if not u.tenant_exists(self.keystone, self.demo_tenant):
 
105
            tenant = self.keystone.tenants.create(tenant_name=self.demo_tenant,
 
106
                                                  description='demo tenant',
 
107
                                                  enabled=True)
 
108
            self.keystone.roles.create(name=self.demo_role)
 
109
            self.keystone.users.create(name=self.demo_user,
 
110
                                       password='password',
 
111
                                       tenant_id=tenant.id,
 
112
                                       email='demo@demo.com')
 
113
 
 
114
        # Authenticate demo user with keystone
 
115
        self.keystone_demo = \
 
116
            u.authenticate_keystone_user(self.keystone, user=self.demo_user,
 
117
                                         password='password',
 
118
                                         tenant=self.demo_tenant)
 
119
 
 
120
    def test_services(self):
 
121
        """Verify the expected services are running on the corresponding
 
122
           service units."""
 
123
        swift_storage_services = ['status swift-account',
 
124
                                  'status swift-account-auditor',
 
125
                                  'status swift-account-reaper',
 
126
                                  'status swift-account-replicator',
 
127
                                  'status swift-container',
 
128
                                  'status swift-container-auditor',
 
129
                                  'status swift-container-replicator',
 
130
                                  'status swift-container-updater',
 
131
                                  'status swift-object',
 
132
                                  'status swift-object-auditor',
 
133
                                  'status swift-object-replicator',
 
134
                                  'status swift-object-updater']
 
135
        if self._get_openstack_release() >= self.precise_icehouse:
 
136
            swift_storage_services.append('status swift-container-sync')
 
137
        commands = {
 
138
            self.mysql_sentry: ['status mysql'],
 
139
            self.keystone_sentry: ['status keystone'],
 
140
            self.glance_sentry: ['status glance-registry', 'status glance-api'],
 
141
            self.swift_proxy_sentry: ['status swift-proxy'],
 
142
            self.swift_storage_sentry: swift_storage_services
 
143
        }
 
144
 
 
145
        ret = u.validate_services(commands)
 
146
        if ret:
 
147
            amulet.raise_status(amulet.FAIL, msg=ret)
 
148
 
 
149
    def test_users(self):
 
150
        """Verify all existing roles."""
 
151
        user1 = {'name': 'demoUser',
 
152
                 'enabled': True,
 
153
                 'tenantId': u.not_null,
 
154
                 'id': u.not_null,
 
155
                 'email': 'demo@demo.com'}
 
156
        user2 = {'name': 'admin',
 
157
                 'enabled': True,
 
158
                 'tenantId': u.not_null,
 
159
                 'id': u.not_null,
 
160
                 'email': 'juju@localhost'}
 
161
        user3 = {'name': 'glance',
 
162
                 'enabled': True,
 
163
                 'tenantId': u.not_null,
 
164
                 'id': u.not_null,
 
165
                 'email': u'juju@localhost'}
 
166
        user4 = {'name': 'swift',
 
167
                 'enabled': True,
 
168
                 'tenantId': u.not_null,
 
169
                 'id': u.not_null,
 
170
                 'email': u'juju@localhost'}
 
171
        expected = [user1, user2, user3, user4]
 
172
        actual = self.keystone.users.list()
 
173
 
 
174
        ret = u.validate_user_data(expected, actual)
 
175
        if ret:
 
176
            amulet.raise_status(amulet.FAIL, msg=ret)
 
177
 
 
178
    def test_service_catalog(self):
 
179
        """Verify that the service catalog endpoint data is valid."""
 
180
        endpoint_vol = {'adminURL': u.valid_url,
 
181
                        'region': 'RegionOne',
 
182
                        'publicURL': u.valid_url,
 
183
                        'internalURL': u.valid_url}
 
184
        endpoint_id = {'adminURL': u.valid_url,
 
185
                       'region': 'RegionOne',
 
186
                       'publicURL': u.valid_url,
 
187
                       'internalURL': u.valid_url}
 
188
        if self._get_openstack_release() >= self.precise_folsom:
 
189
            endpoint_vol['id'] = u.not_null
 
190
            endpoint_id['id'] = u.not_null
 
191
        expected = {'image': [endpoint_id], 'object-store': [endpoint_id],
 
192
                    'identity': [endpoint_id]}
 
193
        actual = self.keystone_demo.service_catalog.get_endpoints()
 
194
 
 
195
        ret = u.validate_svc_catalog_endpoint_data(expected, actual)
 
196
        if ret:
 
197
            amulet.raise_status(amulet.FAIL, msg=ret)
 
198
 
 
199
    def test_openstack_object_store_endpoint(self):
 
200
        """Verify the swift object-store endpoint data."""
 
201
        endpoints = self.keystone.endpoints.list()
 
202
        admin_port = internal_port = public_port = '8080'
 
203
        expected = {'id': u.not_null,
 
204
                    'region': 'RegionOne',
 
205
                    'adminurl': u.valid_url,
 
206
                    'internalurl': u.valid_url,
 
207
                    'publicurl': u.valid_url,
 
208
                    'service_id': u.not_null}
 
209
 
 
210
        ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
 
211
                                       public_port, expected)
 
212
        if ret:
 
213
            message = 'object-store endpoint: {}'.format(ret)
 
214
            amulet.raise_status(amulet.FAIL, msg=message)
 
215
 
 
216
    def test_swift_storage_swift_storage_relation(self):
 
217
        """Verify the swift-storage to swift-proxy swift-storage relation
 
218
           data."""
 
219
        unit = self.swift_storage_sentry
 
220
        relation = ['swift-storage', 'swift-proxy:swift-storage']
 
221
        expected = {
 
222
            'account_port': '6002',
 
223
            'zone': '1',
 
224
            'object_port': '6000',
 
225
            'container_port': '6001',
 
226
            'private-address': u.valid_ip,
 
227
            'device': 'vdb'
 
228
        }
 
229
 
 
230
        ret = u.validate_relation_data(unit, relation, expected)
 
231
        if ret:
 
232
            message = u.relation_error('swift-storage swift-storage', ret)
 
233
            amulet.raise_status(amulet.FAIL, msg=message)
 
234
 
 
235
    def test_swift_proxy_swift_storage_relation(self):
 
236
        """Verify the swift-proxy to swift-storage swift-storage relation
 
237
           data."""
 
238
        unit = self.swift_proxy_sentry
 
239
        relation = ['swift-storage', 'swift-storage:swift-storage']
 
240
        expected = {
 
241
            'private-address': u.valid_ip,
 
242
            'trigger': u.not_null,
 
243
            'rings_url': u.valid_url,
 
244
            'swift_hash': u.not_null
 
245
        }
 
246
 
 
247
        ret = u.validate_relation_data(unit, relation, expected)
 
248
        if ret:
 
249
            message = u.relation_error('swift-proxy swift-storage', ret)
 
250
            amulet.raise_status(amulet.FAIL, msg=message)
 
251
 
 
252
    def test_restart_on_config_change(self):
 
253
        """Verify that the specified services are restarted when the config
 
254
           is changed."""
 
255
        # NOTE(coreycb): Skipping failing test on until resolved. This test
 
256
        #                fails because the config file's last mod time is
 
257
        #                slightly after the process' last mod time.
 
258
        if self._get_openstack_release() >= self.precise_essex:
 
259
            u.log.error("Skipping failing test until resolved")
 
260
            return
 
261
 
 
262
        services = {'swift-account-server': 'account-server.conf',
 
263
                    'swift-account-auditor': 'account-server.conf',
 
264
                    'swift-account-reaper': 'account-server.conf',
 
265
                    'swift-account-replicator': 'account-server.conf',
 
266
                    'swift-container-server': 'container-server.conf',
 
267
                    'swift-container-auditor': 'container-server.conf',
 
268
                    'swift-container-replicator': 'container-server.conf',
 
269
                    'swift-container-updater': 'container-server.conf',
 
270
                    'swift-object-server': 'object-server.conf',
 
271
                    'swift-object-auditor': 'object-server.conf',
 
272
                    'swift-object-replicator': 'object-server.conf',
 
273
                    'swift-object-updater': 'object-server.conf'}
 
274
        if self._get_openstack_release() >= self.precise_icehouse:
 
275
            services['swift-container-sync'] = 'container-server.conf'
 
276
 
 
277
        self.d.configure('swift-storage',
 
278
                         {'object-server-threads-per-disk': '2'})
 
279
 
 
280
        time = 20
 
281
        for s, conf in services.iteritems():
 
282
            config = '/etc/swift/{}'.format(conf)
 
283
            if not u.service_restarted(self.swift_storage_sentry, s, config,
 
284
                                       pgrep_full=True, sleep_time=time):
 
285
                msg = "service {} didn't restart after config change".format(s)
 
286
                amulet.raise_status(amulet.FAIL, msg=msg)
 
287
            time = 0
 
288
 
 
289
        self.d.configure('swift-storage',
 
290
                         {'object-server-threads-per-disk': '4'})
 
291
 
 
292
    def test_swift_config(self):
 
293
        """Verify the data in the swift-hash section of the swift config
 
294
           file."""
 
295
        unit = self.swift_storage_sentry
 
296
        conf = '/etc/swift/swift.conf'
 
297
        swift_proxy_relation = self.swift_proxy_sentry.relation('swift-storage',
 
298
                                                  'swift-storage:swift-storage')
 
299
        expected = {
 
300
            'swift_hash_path_suffix': swift_proxy_relation['swift_hash']
 
301
        }
 
302
 
 
303
        ret = u.validate_config_data(unit, conf, 'swift-hash', expected)
 
304
        if ret:
 
305
            message = "swift config error: {}".format(ret)
 
306
            amulet.raise_status(amulet.FAIL, msg=message)
 
307
 
 
308
    def test_account_server_config(self):
 
309
        """Verify the data in the account server config file."""
 
310
        unit = self.swift_storage_sentry
 
311
        conf = '/etc/swift/account-server.conf'
 
312
        expected = {
 
313
            'DEFAULT': {
 
314
                'bind_ip': '0.0.0.0',
 
315
                'bind_port': '6002',
 
316
                'workers': '1'
 
317
            },
 
318
            'pipeline:main': {
 
319
                'pipeline': 'recon account-server'
 
320
            },
 
321
            'filter:recon': {
 
322
                'use': 'egg:swift#recon',
 
323
                'recon_cache_path': '/var/cache/swift'
 
324
            },
 
325
            'app:account-server': {
 
326
                'use': 'egg:swift#account'
 
327
            }
 
328
        }
 
329
 
 
330
        for section, pairs in expected.iteritems():
 
331
            ret = u.validate_config_data(unit, conf, section, pairs)
 
332
            if ret:
 
333
                message = "account server config error: {}".format(ret)
 
334
                amulet.raise_status(amulet.FAIL, msg=message)
 
335
 
 
336
    def test_container_server_config(self):
 
337
        """Verify the data in the container server config file."""
 
338
        unit = self.swift_storage_sentry
 
339
        conf = '/etc/swift/container-server.conf'
 
340
        expected = {
 
341
            'DEFAULT': {
 
342
                'bind_ip': '0.0.0.0',
 
343
                'bind_port': '6001',
 
344
                'workers': '1'
 
345
            },
 
346
            'pipeline:main': {
 
347
                'pipeline': 'recon container-server'
 
348
            },
 
349
            'filter:recon': {
 
350
                'use': 'egg:swift#recon',
 
351
                'recon_cache_path': '/var/cache/swift'
 
352
            },
 
353
            'app:container-server': {
 
354
                'use': 'egg:swift#container',
 
355
                'allow_versions': 'true'
 
356
            }
 
357
        }
 
358
 
 
359
        for section, pairs in expected.iteritems():
 
360
            ret = u.validate_config_data(unit, conf, section, pairs)
 
361
            if ret:
 
362
                message = "container server config error: {}".format(ret)
 
363
                amulet.raise_status(amulet.FAIL, msg=message)
 
364
 
 
365
    def test_object_server_config(self):
 
366
        """Verify the data in the object server config file."""
 
367
        unit = self.swift_storage_sentry
 
368
        conf = '/etc/swift/object-server.conf'
 
369
        expected = {
 
370
            'DEFAULT': {
 
371
                'bind_ip': '0.0.0.0',
 
372
                'bind_port': '6000',
 
373
                'workers': '1'
 
374
            },
 
375
            'pipeline:main': {
 
376
                'pipeline': 'recon object-server'
 
377
            },
 
378
            'filter:recon': {
 
379
                'use': 'egg:swift#recon',
 
380
                'recon_cache_path': '/var/cache/swift'
 
381
            },
 
382
            'app:object-server': {
 
383
                'use': 'egg:swift#object',
 
384
                'threads_per_disk': '4'
 
385
            }
 
386
        }
 
387
 
 
388
        for section, pairs in expected.iteritems():
 
389
            ret = u.validate_config_data(unit, conf, section, pairs)
 
390
            if ret:
 
391
                message = "object server config error: {}".format(ret)
 
392
                amulet.raise_status(amulet.FAIL, msg=message)
 
393
 
 
394
    def test_image_create(self):
 
395
        """Create an instance in glance, which is backed by swift, and validate
 
396
           that some of the metadata for the image match in glance and swift."""
 
397
        # NOTE(coreycb): Skipping failing test on folsom until resolved. On
 
398
        #                folsom only, uploading an image to glance gets 400 Bad
 
399
        #                Request - Error uploading image: (error): [Errno 111]
 
400
        #                ECONNREFUSED (HTTP 400)
 
401
        if self._get_openstack_release() == self.precise_folsom:
 
402
            u.log.error("Skipping failing test until resolved")
 
403
            return
 
404
 
 
405
        # Create glance image
 
406
        image = u.create_cirros_image(self.glance, "cirros-image")
 
407
        if not image:
 
408
            amulet.raise_status(amulet.FAIL, msg="Image create failed")
 
409
 
 
410
        # Validate that cirros image exists in glance and get its checksum/size
 
411
        images = list(self.glance.images.list())
 
412
        if len(images) != 1:
 
413
            msg = "Expected 1 glance image, found {}".format(len(images))
 
414
            amulet.raise_status(amulet.FAIL, msg=msg)
 
415
 
 
416
        if images[0].name != 'cirros-image':
 
417
            message = "cirros image does not exist"
 
418
            amulet.raise_status(amulet.FAIL, msg=message)
 
419
 
 
420
        glance_image_md5 = image.checksum
 
421
        glance_image_size = image.size
 
422
 
 
423
        # Validate that swift object's checksum/size match that from glance
 
424
        headers, containers = self.swift.get_account()
 
425
        if len(containers) != 1:
 
426
            msg = "Expected 1 swift container, found {}".format(len(containers))
 
427
            amulet.raise_status(amulet.FAIL, msg=msg)
 
428
 
 
429
        container_name = containers[0].get('name')
 
430
 
 
431
        headers, objects = self.swift.get_container(container_name)
 
432
        if len(objects) != 1:
 
433
            msg = "Expected 1 swift object, found {}".format(len(objects))
 
434
            amulet.raise_status(amulet.FAIL, msg=msg)
 
435
 
 
436
        swift_object_size = objects[0].get('bytes')
 
437
        swift_object_md5 = objects[0].get('hash')
 
438
 
 
439
        if glance_image_size != swift_object_size:
 
440
            msg = "Glance image size {} != swift object size {}".format( \
 
441
                                           glance_image_size, swift_object_size)
 
442
            amulet.raise_status(amulet.FAIL, msg=msg)
 
443
 
 
444
        if glance_image_md5 != swift_object_md5:
 
445
            msg = "Glance image hash {} != swift object hash {}".format( \
 
446
                                             glance_image_md5, swift_object_md5)
 
447
            amulet.raise_status(amulet.FAIL, msg=msg)
 
448
 
 
449
        # Cleanup
 
450
        u.delete_image(self.glance, image)