~1chb1n/charms/trusty/ceph-osd/stable-flip-tests-helper-syncs

« back to all changes in this revision

Viewing changes to tests/basic_deployment.py

  • Committer: james.page at ubuntu
  • Date: 2015-08-10 16:33:15 UTC
  • Revision ID: james.page@ubuntu.com-20150810163315-5b5oocs7c40qcw4g
Tags: 15.07
[gnuoy] 15.07 Charm release

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python import amulet
 
1
#!/usr/bin/python
2
2
 
3
3
import amulet
 
4
import time
4
5
from charmhelpers.contrib.openstack.amulet.deployment import (
5
6
    OpenStackAmuletDeployment
6
7
)
7
 
from charmhelpers.contrib.openstack.amulet.utils import (  # noqa
 
8
from charmhelpers.contrib.openstack.amulet.utils import (
8
9
    OpenStackAmuletUtils,
9
10
    DEBUG,
10
 
    ERROR
 
11
    # ERROR
11
12
)
12
13
 
13
14
# Use DEBUG to turn on debug logging
18
19
    """Amulet tests on a basic ceph-osd deployment."""
19
20
 
20
21
    def __init__(self, series=None, openstack=None, source=None,
21
 
                 stable=True):
 
22
                 stable=False):
22
23
        """Deploy the entire test environment."""
23
24
        super(CephOsdBasicDeployment, self).__init__(series, openstack,
24
25
                                                     source, stable)
36
37
           compatible with the local charm (e.g. stable or next).
37
38
           """
38
39
        this_service = {'name': 'ceph-osd'}
39
 
        other_services = [{'name': 'ceph', 'units': 3}, {'name': 'mysql'},
40
 
                          {'name': 'keystone'}, {'name': 'rabbitmq-server'},
41
 
                          {'name': 'nova-compute'}, {'name': 'glance'},
 
40
        other_services = [{'name': 'ceph', 'units': 3},
 
41
                          {'name': 'mysql'},
 
42
                          {'name': 'keystone'},
 
43
                          {'name': 'rabbitmq-server'},
 
44
                          {'name': 'nova-compute'},
 
45
                          {'name': 'glance'},
42
46
                          {'name': 'cinder'}]
43
47
        super(CephOsdBasicDeployment, self)._add_services(this_service,
44
48
                                                          other_services)
98
102
        self.mysql_sentry = self.d.sentry.unit['mysql/0']
99
103
        self.keystone_sentry = self.d.sentry.unit['keystone/0']
100
104
        self.rabbitmq_sentry = self.d.sentry.unit['rabbitmq-server/0']
101
 
        self.nova_compute_sentry = self.d.sentry.unit['nova-compute/0']
 
105
        self.nova_sentry = self.d.sentry.unit['nova-compute/0']
102
106
        self.glance_sentry = self.d.sentry.unit['glance/0']
103
107
        self.cinder_sentry = self.d.sentry.unit['cinder/0']
104
108
        self.ceph0_sentry = self.d.sentry.unit['ceph/0']
105
109
        self.ceph1_sentry = self.d.sentry.unit['ceph/1']
106
110
        self.ceph2_sentry = self.d.sentry.unit['ceph/2']
107
111
        self.ceph_osd_sentry = self.d.sentry.unit['ceph-osd/0']
 
112
        u.log.debug('openstack release val: {}'.format(
 
113
            self._get_openstack_release()))
 
114
        u.log.debug('openstack release str: {}'.format(
 
115
            self._get_openstack_release_string()))
 
116
 
 
117
        # Let things settle a bit original moving forward
 
118
        time.sleep(30)
108
119
 
109
120
        # Authenticate admin with keystone
110
121
        self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
112
123
                                                      password='openstack',
113
124
                                                      tenant='admin')
114
125
 
 
126
        # Authenticate admin with cinder endpoint
 
127
        self.cinder = u.authenticate_cinder_admin(self.keystone_sentry,
 
128
                                                  username='admin',
 
129
                                                  password='openstack',
 
130
                                                  tenant='admin')
115
131
        # Authenticate admin with glance endpoint
116
132
        self.glance = u.authenticate_glance_admin(self.keystone)
117
133
 
 
134
        # Authenticate admin with nova endpoint
 
135
        self.nova = u.authenticate_nova_user(self.keystone,
 
136
                                             user='admin',
 
137
                                             password='openstack',
 
138
                                             tenant='admin')
 
139
 
118
140
        # Create a demo tenant/role/user
119
141
        self.demo_tenant = 'demoTenant'
120
142
        self.demo_role = 'demoRole'
141
163
                                                  'password',
142
164
                                                  self.demo_tenant)
143
165
 
144
 
    def _ceph_osd_id(self, index):
145
 
        """Produce a shell command that will return a ceph-osd id."""
146
 
        return "`initctl list | grep 'ceph-osd ' | awk 'NR=={} {{ print $2 }}' | grep -o '[0-9]*'`".format(index + 1)  # noqa
147
 
 
148
 
    def test_services(self):
 
166
    def test_100_ceph_processes(self):
 
167
        """Verify that the expected service processes are running
 
168
        on each ceph unit."""
 
169
 
 
170
        # Process name and quantity of processes to expect on each unit
 
171
        ceph_processes = {
 
172
            'ceph-mon': 1,
 
173
            'ceph-osd': 2
 
174
        }
 
175
 
 
176
        # Units with process names and PID quantities expected
 
177
        expected_processes = {
 
178
            self.ceph0_sentry: ceph_processes,
 
179
            self.ceph1_sentry: ceph_processes,
 
180
            self.ceph2_sentry: ceph_processes,
 
181
            self.ceph_osd_sentry: {'ceph-osd': 2}
 
182
        }
 
183
 
 
184
        actual_pids = u.get_unit_process_ids(expected_processes)
 
185
        ret = u.validate_unit_process_ids(expected_processes, actual_pids)
 
186
        if ret:
 
187
            amulet.raise_status(amulet.FAIL, msg=ret)
 
188
 
 
189
    def test_102_services(self):
149
190
        """Verify the expected services are running on the service units."""
150
 
        commands = {
151
 
            self.mysql_sentry: ['status mysql'],
152
 
            self.rabbitmq_sentry: ['sudo service rabbitmq-server status'],
153
 
            self.nova_compute_sentry: ['status nova-compute'],
154
 
            self.keystone_sentry: ['status keystone'],
155
 
            self.glance_sentry: ['status glance-registry',
156
 
                                 'status glance-api'],
157
 
            self.cinder_sentry: ['status cinder-api',
158
 
                                 'status cinder-scheduler',
159
 
                                 'status cinder-volume']
 
191
 
 
192
        services = {
 
193
            self.mysql_sentry: ['mysql'],
 
194
            self.rabbitmq_sentry: ['rabbitmq-server'],
 
195
            self.nova_sentry: ['nova-compute'],
 
196
            self.keystone_sentry: ['keystone'],
 
197
            self.glance_sentry: ['glance-registry',
 
198
                                 'glance-api'],
 
199
            self.cinder_sentry: ['cinder-api',
 
200
                                 'cinder-scheduler',
 
201
                                 'cinder-volume'],
160
202
        }
161
 
        ceph_services = ['status ceph-mon-all',
162
 
                         'status ceph-mon id=`hostname`']
163
 
        ceph_osd0 = 'status ceph-osd id={}'.format(self._ceph_osd_id(0))
164
 
        ceph_osd1 = 'status ceph-osd id={}'.format(self._ceph_osd_id(1))
165
 
        ceph_osd_services = [ceph_osd0, ceph_osd1, 'status ceph-osd-all']
166
 
        ceph_services.extend(ceph_osd_services)
167
 
        commands[self.ceph0_sentry] = ceph_services
168
 
        commands[self.ceph1_sentry] = ceph_services
169
 
        commands[self.ceph2_sentry] = ceph_services
170
 
        commands[self.ceph_osd_sentry] = ceph_osd_services
171
 
 
172
 
        ret = u.validate_services(commands)
 
203
 
 
204
        if self._get_openstack_release() < self.vivid_kilo:
 
205
            # For upstart systems only.  Ceph services under systemd
 
206
            # are checked by process name instead.
 
207
            ceph_services = [
 
208
                'ceph-mon-all',
 
209
                'ceph-mon id=`hostname`',
 
210
                'ceph-osd-all',
 
211
                'ceph-osd id={}'.format(u.get_ceph_osd_id_cmd(0)),
 
212
                'ceph-osd id={}'.format(u.get_ceph_osd_id_cmd(1))
 
213
            ]
 
214
            services[self.ceph0_sentry] = ceph_services
 
215
            services[self.ceph1_sentry] = ceph_services
 
216
            services[self.ceph2_sentry] = ceph_services
 
217
            services[self.ceph_osd_sentry] = [
 
218
                'ceph-osd-all',
 
219
                'ceph-osd id={}'.format(u.get_ceph_osd_id_cmd(0)),
 
220
                'ceph-osd id={}'.format(u.get_ceph_osd_id_cmd(1))
 
221
            ]
 
222
 
 
223
        ret = u.validate_services_by_name(services)
173
224
        if ret:
174
225
            amulet.raise_status(amulet.FAIL, msg=ret)
175
226
 
176
 
    def test_ceph_osd_ceph_relation(self):
 
227
    def test_200_ceph_osd_ceph_relation(self):
177
228
        """Verify the ceph-osd to ceph relation data."""
 
229
        u.log.debug('Checking ceph-osd:ceph mon relation data...')
178
230
        unit = self.ceph_osd_sentry
179
231
        relation = ['mon', 'ceph:osd']
180
232
        expected = {
186
238
            message = u.relation_error('ceph-osd to ceph', ret)
187
239
            amulet.raise_status(amulet.FAIL, msg=message)
188
240
 
189
 
    def test_ceph0_to_ceph_osd_relation(self):
 
241
    def test_201_ceph0_to_ceph_osd_relation(self):
190
242
        """Verify the ceph0 to ceph-osd relation data."""
 
243
        u.log.debug('Checking ceph0:ceph-osd mon relation data...')
191
244
        unit = self.ceph0_sentry
192
245
        relation = ['osd', 'ceph-osd:mon']
193
246
        expected = {
203
256
            message = u.relation_error('ceph0 to ceph-osd', ret)
204
257
            amulet.raise_status(amulet.FAIL, msg=message)
205
258
 
206
 
    def test_ceph1_to_ceph_osd_relation(self):
 
259
    def test_202_ceph1_to_ceph_osd_relation(self):
207
260
        """Verify the ceph1 to ceph-osd relation data."""
 
261
        u.log.debug('Checking ceph1:ceph-osd mon relation data...')
208
262
        unit = self.ceph1_sentry
209
263
        relation = ['osd', 'ceph-osd:mon']
210
264
        expected = {
220
274
            message = u.relation_error('ceph1 to ceph-osd', ret)
221
275
            amulet.raise_status(amulet.FAIL, msg=message)
222
276
 
223
 
    def test_ceph2_to_ceph_osd_relation(self):
 
277
    def test_203_ceph2_to_ceph_osd_relation(self):
224
278
        """Verify the ceph2 to ceph-osd relation data."""
 
279
        u.log.debug('Checking ceph2:ceph-osd mon relation data...')
225
280
        unit = self.ceph2_sentry
226
281
        relation = ['osd', 'ceph-osd:mon']
227
282
        expected = {
237
292
            message = u.relation_error('ceph2 to ceph-osd', ret)
238
293
            amulet.raise_status(amulet.FAIL, msg=message)
239
294
 
240
 
    def test_ceph_config(self):
 
295
    def test_300_ceph_osd_config(self):
241
296
        """Verify the data in the ceph config file."""
 
297
        u.log.debug('Checking ceph config file data...')
242
298
        unit = self.ceph_osd_sentry
243
299
        conf = '/etc/ceph/ceph.conf'
244
300
        expected = {
271
327
                message = "ceph config error: {}".format(ret)
272
328
                amulet.raise_status(amulet.FAIL, msg=message)
273
329
 
274
 
    def test_restart_on_config_change(self):
275
 
        """Verify the specified services are restarted on config change."""
276
 
        # NOTE(coreycb): Test not implemented but should it be? ceph-osd svcs
277
 
        #                aren't restarted by charm after config change.  Should
278
 
        #                they be restarted?
279
 
        if self._get_openstack_release() >= self.precise_essex:
280
 
            u.log.error("Test not implemented")
281
 
            return
 
330
    def test_302_cinder_rbd_config(self):
 
331
        """Verify the cinder config file data regarding ceph."""
 
332
        u.log.debug('Checking cinder (rbd) config file data...')
 
333
        unit = self.cinder_sentry
 
334
        conf = '/etc/cinder/cinder.conf'
 
335
        expected = {
 
336
            'DEFAULT': {
 
337
                'volume_driver': 'cinder.volume.drivers.rbd.RBDDriver'
 
338
            }
 
339
        }
 
340
        for section, pairs in expected.iteritems():
 
341
            ret = u.validate_config_data(unit, conf, section, pairs)
 
342
            if ret:
 
343
                message = "cinder (rbd) config error: {}".format(ret)
 
344
                amulet.raise_status(amulet.FAIL, msg=message)
 
345
 
 
346
    def test_304_glance_rbd_config(self):
 
347
        """Verify the glance config file data regarding ceph."""
 
348
        u.log.debug('Checking glance (rbd) config file data...')
 
349
        unit = self.glance_sentry
 
350
        conf = '/etc/glance/glance-api.conf'
 
351
        config = {
 
352
            'default_store': 'rbd',
 
353
            'rbd_store_ceph_conf': '/etc/ceph/ceph.conf',
 
354
            'rbd_store_user': 'glance',
 
355
            'rbd_store_pool': 'glance',
 
356
            'rbd_store_chunk_size': '8'
 
357
        }
 
358
 
 
359
        if self._get_openstack_release() >= self.trusty_kilo:
 
360
            # Kilo or later
 
361
            config['stores'] = ('glance.store.filesystem.Store,'
 
362
                                'glance.store.http.Store,'
 
363
                                'glance.store.rbd.Store')
 
364
            section = 'glance_store'
 
365
        else:
 
366
            # Juno or earlier
 
367
            section = 'DEFAULT'
 
368
 
 
369
        expected = {section: config}
 
370
        for section, pairs in expected.iteritems():
 
371
            ret = u.validate_config_data(unit, conf, section, pairs)
 
372
            if ret:
 
373
                message = "glance (rbd) config error: {}".format(ret)
 
374
                amulet.raise_status(amulet.FAIL, msg=message)
 
375
 
 
376
    def test_306_nova_rbd_config(self):
 
377
        """Verify the nova config file data regarding ceph."""
 
378
        u.log.debug('Checking nova (rbd) config file data...')
 
379
        unit = self.nova_sentry
 
380
        conf = '/etc/nova/nova.conf'
 
381
        expected = {
 
382
            'libvirt': {
 
383
                'rbd_pool': 'nova',
 
384
                'rbd_user': 'nova-compute',
 
385
                'rbd_secret_uuid': u.not_null
 
386
            }
 
387
        }
 
388
        for section, pairs in expected.iteritems():
 
389
            ret = u.validate_config_data(unit, conf, section, pairs)
 
390
            if ret:
 
391
                message = "nova (rbd) config error: {}".format(ret)
 
392
                amulet.raise_status(amulet.FAIL, msg=message)
 
393
 
 
394
    def test_400_ceph_check_osd_pools(self):
 
395
        """Check osd pools on all ceph units, expect them to be
 
396
        identical, and expect specific pools to be present."""
 
397
        u.log.debug('Checking pools on ceph units...')
 
398
 
 
399
        expected_pools = self.get_ceph_expected_pools()
 
400
        results = []
 
401
        sentries = [
 
402
            self.ceph_osd_sentry,
 
403
            self.ceph0_sentry,
 
404
            self.ceph1_sentry,
 
405
            self.ceph2_sentry
 
406
        ]
 
407
 
 
408
        # Check for presence of expected pools on each unit
 
409
        u.log.debug('Expected pools: {}'.format(expected_pools))
 
410
        for sentry_unit in sentries:
 
411
            pools = u.get_ceph_pools(sentry_unit)
 
412
            results.append(pools)
 
413
 
 
414
            for expected_pool in expected_pools:
 
415
                if expected_pool not in pools:
 
416
                    msg = ('{} does not have pool: '
 
417
                           '{}'.format(sentry_unit.info['unit_name'],
 
418
                                       expected_pool))
 
419
                    amulet.raise_status(amulet.FAIL, msg=msg)
 
420
            u.log.debug('{} has (at least) the expected '
 
421
                        'pools.'.format(sentry_unit.info['unit_name']))
 
422
 
 
423
        # Check that all units returned the same pool name:id data
 
424
        ret = u.validate_list_of_identical_dicts(results)
 
425
        if ret:
 
426
            u.log.debug('Pool list results: {}'.format(results))
 
427
            msg = ('{}; Pool list results are not identical on all '
 
428
                   'ceph units.'.format(ret))
 
429
            amulet.raise_status(amulet.FAIL, msg=msg)
 
430
        else:
 
431
            u.log.debug('Pool list on all ceph units produced the '
 
432
                        'same results (OK).')
 
433
 
 
434
    def test_410_ceph_cinder_vol_create(self):
 
435
        """Create and confirm a ceph-backed cinder volume, and inspect
 
436
        ceph cinder pool object count as the volume is created
 
437
        and deleted."""
 
438
        sentry_unit = self.ceph0_sentry
 
439
        obj_count_samples = []
 
440
        pool_size_samples = []
 
441
        pools = u.get_ceph_pools(self.ceph0_sentry)
 
442
        cinder_pool = pools['cinder']
 
443
 
 
444
        # Check ceph cinder pool object count, disk space usage and pool name
 
445
        u.log.debug('Checking ceph cinder pool original samples...')
 
446
        pool_name, obj_count, kb_used = u.get_ceph_pool_sample(sentry_unit,
 
447
                                                               cinder_pool)
 
448
        obj_count_samples.append(obj_count)
 
449
        pool_size_samples.append(kb_used)
 
450
 
 
451
        expected = 'cinder'
 
452
        if pool_name != expected:
 
453
            msg = ('Ceph pool {} unexpected name (actual, expected): '
 
454
                   '{}. {}'.format(cinder_pool, pool_name, expected))
 
455
            amulet.raise_status(amulet.FAIL, msg=msg)
 
456
 
 
457
        # Create ceph-backed cinder volume
 
458
        cinder_vol = u.create_cinder_volume(self.cinder)
 
459
 
 
460
        # Re-check ceph cinder pool object count and disk usage
 
461
        time.sleep(10)
 
462
        u.log.debug('Checking ceph cinder pool samples after volume create...')
 
463
        pool_name, obj_count, kb_used = u.get_ceph_pool_sample(sentry_unit,
 
464
                                                               cinder_pool)
 
465
        obj_count_samples.append(obj_count)
 
466
        pool_size_samples.append(kb_used)
 
467
 
 
468
        # Delete ceph-backed cinder volume
 
469
        u.delete_resource(self.cinder.volumes, cinder_vol, msg="cinder volume")
 
470
 
 
471
        # Final check, ceph cinder pool object count and disk usage
 
472
        time.sleep(10)
 
473
        u.log.debug('Checking ceph cinder pool after volume delete...')
 
474
        pool_name, obj_count, kb_used = u.get_ceph_pool_sample(sentry_unit,
 
475
                                                               cinder_pool)
 
476
        obj_count_samples.append(obj_count)
 
477
        pool_size_samples.append(kb_used)
 
478
 
 
479
        # Validate ceph cinder pool object count samples over time
 
480
        ret = u.validate_ceph_pool_samples(obj_count_samples,
 
481
                                           "cinder pool object count")
 
482
        if ret:
 
483
            amulet.raise_status(amulet.FAIL, msg=ret)
 
484
 
 
485
        # Validate ceph cinder pool disk space usage samples over time
 
486
        ret = u.validate_ceph_pool_samples(pool_size_samples,
 
487
                                           "cinder pool disk usage")
 
488
        if ret:
 
489
            amulet.raise_status(amulet.FAIL, msg=ret)
 
490
 
 
491
    def test_412_ceph_glance_image_create_delete(self):
 
492
        """Create and confirm a ceph-backed glance image, and inspect
 
493
        ceph glance pool object count as the image is created
 
494
        and deleted."""
 
495
        sentry_unit = self.ceph0_sentry
 
496
        obj_count_samples = []
 
497
        pool_size_samples = []
 
498
        pools = u.get_ceph_pools(self.ceph0_sentry)
 
499
        glance_pool = pools['glance']
 
500
 
 
501
        # Check ceph glance pool object count, disk space usage and pool name
 
502
        u.log.debug('Checking ceph glance pool original samples...')
 
503
        pool_name, obj_count, kb_used = u.get_ceph_pool_sample(sentry_unit,
 
504
                                                               glance_pool)
 
505
        obj_count_samples.append(obj_count)
 
506
        pool_size_samples.append(kb_used)
 
507
 
 
508
        expected = 'glance'
 
509
        if pool_name != expected:
 
510
            msg = ('Ceph glance pool {} unexpected name (actual, '
 
511
                   'expected): {}. {}'.format(glance_pool,
 
512
                                              pool_name, expected))
 
513
            amulet.raise_status(amulet.FAIL, msg=msg)
 
514
 
 
515
        # Create ceph-backed glance image
 
516
        glance_img = u.create_cirros_image(self.glance, 'cirros-image-1')
 
517
 
 
518
        # Re-check ceph glance pool object count and disk usage
 
519
        time.sleep(10)
 
520
        u.log.debug('Checking ceph glance pool samples after image create...')
 
521
        pool_name, obj_count, kb_used = u.get_ceph_pool_sample(sentry_unit,
 
522
                                                               glance_pool)
 
523
        obj_count_samples.append(obj_count)
 
524
        pool_size_samples.append(kb_used)
 
525
 
 
526
        # Delete ceph-backed glance image
 
527
        u.delete_resource(self.glance.images,
 
528
                          glance_img, msg="glance image")
 
529
 
 
530
        # Final check, ceph glance pool object count and disk usage
 
531
        time.sleep(10)
 
532
        u.log.debug('Checking ceph glance pool samples after image delete...')
 
533
        pool_name, obj_count, kb_used = u.get_ceph_pool_sample(sentry_unit,
 
534
                                                               glance_pool)
 
535
        obj_count_samples.append(obj_count)
 
536
        pool_size_samples.append(kb_used)
 
537
 
 
538
        # Validate ceph glance pool object count samples over time
 
539
        ret = u.validate_ceph_pool_samples(obj_count_samples,
 
540
                                           "glance pool object count")
 
541
        if ret:
 
542
            amulet.raise_status(amulet.FAIL, msg=ret)
 
543
 
 
544
        # Validate ceph glance pool disk space usage samples over time
 
545
        ret = u.validate_ceph_pool_samples(pool_size_samples,
 
546
                                           "glance pool disk usage")
 
547
        if ret:
 
548
            amulet.raise_status(amulet.FAIL, msg=ret)
 
549
 
 
550
    def test_499_ceph_cmds_exit_zero(self):
 
551
        """Check basic functionality of ceph cli commands against
 
552
        all ceph units."""
 
553
        sentry_units = [
 
554
            self.ceph_osd_sentry,
 
555
            self.ceph0_sentry,
 
556
            self.ceph1_sentry,
 
557
            self.ceph2_sentry
 
558
        ]
 
559
        commands = [
 
560
            'sudo ceph health',
 
561
            'sudo ceph mds stat',
 
562
            'sudo ceph pg stat',
 
563
            'sudo ceph osd stat',
 
564
            'sudo ceph mon stat',
 
565
        ]
 
566
        ret = u.check_commands_on_units(commands, sentry_units)
 
567
        if ret:
 
568
            amulet.raise_status(amulet.FAIL, msg=ret)
 
569
 
 
570
    # FYI: No restart check as ceph services do not restart
 
571
    # when charm config changes, unless monitor count increases.