~openstack-charmers-archive/charms/trusty/cinder/next

« back to all changes in this revision

Viewing changes to tests/basic_deployment.py

  • Committer: Corey Bryant
  • Date: 2015-07-02 15:19:31 UTC
  • mfrom: (98.2.10 cinder)
  • Revision ID: corey.bryant@canonical.com-20150702151931-wjsggzmzi8cyf4yu
[beisner,r=corey.bryant] Amulet test updates.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 
3
3
import amulet
4
4
import os
5
 
import types
6
 
from time import sleep
 
5
import time
7
6
import yaml
8
 
import cinderclient.v1.client as cinder_client
9
7
 
10
8
from charmhelpers.contrib.openstack.amulet.deployment import (
11
9
    OpenStackAmuletDeployment
12
10
)
13
11
 
14
 
from charmhelpers.contrib.openstack.amulet.utils import (  # noqa
 
12
from charmhelpers.contrib.openstack.amulet.utils import (
15
13
    OpenStackAmuletUtils,
16
14
    DEBUG,
17
 
    ERROR
 
15
    # ERROR
18
16
)
19
17
 
20
18
# Use DEBUG to turn on debug logging
22
20
 
23
21
 
24
22
class CinderBasicDeployment(OpenStackAmuletDeployment):
25
 
    '''Amulet tests on a basic lvm-backed cinder deployment. Verify
 
23
    """Amulet tests on a basic lvm-backed cinder deployment. Verify
26
24
       relations, service status, users and endpoint service catalog.
27
25
       Create, clone, delete volumes. Create volume from glance image.
28
 
       Create volume snapshot. Create volume from snapshot.'''
29
 
 
30
 
    # NOTE(beisner):  Features and tests vary across Openstack releases.
31
 
    # https://wiki.openstack.org/wiki/CinderSupportMatrix
 
26
       Create volume snapshot. Create volume from snapshot."""
32
27
 
33
28
    def __init__(self, series=None, openstack=None, source=None, git=False,
34
29
                 stable=False):
35
 
        '''Deploy the entire test environment.'''
 
30
        """Deploy the entire test environment."""
36
31
        super(CinderBasicDeployment, self).__init__(series, openstack, source,
37
32
                                                    stable)
38
33
        self.git = git
56
51
                                                         other_services)
57
52
 
58
53
    def _add_relations(self):
59
 
        '''Add relations for the services.'''
 
54
        """Add relations for the services."""
60
55
        relations = {
61
56
            'keystone:shared-db': 'mysql:shared-db',
62
57
            'cinder:shared-db': 'mysql:shared-db',
70
65
        super(CinderBasicDeployment, self)._add_relations(relations)
71
66
 
72
67
    def _configure_services(self):
73
 
        '''Configure all of the services.'''
 
68
        """Configure all of the services."""
74
69
        cinder_config = {'block-device': 'vdb',
75
70
                         'glance-api-version': '2',
76
71
                         'overwrite': 'true'}
102
97
        super(CinderBasicDeployment, self)._configure_services(configs)
103
98
 
104
99
    def _initialize_tests(self):
105
 
        '''Perform final initialization before tests get run.'''
 
100
        """Perform final initialization before tests get run."""
106
101
        # Access the sentries for inspecting service units
107
102
        self.cinder_sentry = self.d.sentry.unit['cinder/0']
108
103
        self.glance_sentry = self.d.sentry.unit['glance/0']
109
104
        self.mysql_sentry = self.d.sentry.unit['mysql/0']
110
105
        self.keystone_sentry = self.d.sentry.unit['keystone/0']
111
106
        self.rabbitmq_sentry = self.d.sentry.unit['rabbitmq-server/0']
 
107
        u.log.debug('openstack release val: {}'.format(
 
108
            self._get_openstack_release()))
 
109
        u.log.debug('openstack release str: {}'.format(
 
110
            self._get_openstack_release_string()))
 
111
 
 
112
        # Let things settle a bit original moving forward
 
113
        time.sleep(30)
112
114
 
113
115
        # Authenticate admin with keystone
114
116
        self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
115
117
                                                      user='admin',
116
118
                                                      password='openstack',
117
119
                                                      tenant='admin')
 
120
 
118
121
        # Authenticate admin with cinder endpoint
119
 
        self.cinder = self.authenticate_cinder_admin(username='admin',
120
 
                                                     password='openstack',
121
 
                                                     tenant='admin')
 
122
        self.cinder = u.authenticate_cinder_admin(self.keystone_sentry,
 
123
                                                  username='admin',
 
124
                                                  password='openstack',
 
125
                                                  tenant='admin')
 
126
 
122
127
        # Authenticate admin with glance endpoint
123
128
        self.glance = u.authenticate_glance_admin(self.keystone)
124
129
 
125
 
        u.log.debug('openstack rel: {}'.format(self._get_openstack_release()))
126
 
        # Wait for relations to settle
127
 
        sleep(120)
128
 
 
129
 
    def authenticate_cinder_admin(self, username, password, tenant):
130
 
        """Authenticates admin user with cinder."""
131
 
        # NOTE(beisner): need to move to charmhelpers, and adjust calls here.
132
 
        # Probably useful on other charm tests.
133
 
        service_ip = \
134
 
            self.keystone_sentry.relation('shared-db',
135
 
                                          'mysql:shared-db')['private-address']
136
 
        ept = "http://{}:5000/v2.0".format(service_ip.strip().decode('utf-8'))
137
 
        return cinder_client.Client(username, password, tenant, ept)
138
 
 
139
 
    def force_list(self, obj):
140
 
        '''Determine the object type and return a list.  Some Openstack
141
 
           component API list methods return generators, some return lists.
142
 
           Where obj is cinder.volumes, cinder.volume_snapshots, glance.images,
143
 
           or other Openstack object with a list method.'''
144
 
        # NOTE(beisner): need to move to charmhelpers, and adjust calls here.
145
 
 
146
 
        # NOTE(beisner):  Beware - glance's list method returns a generator,
147
 
        # and cinder's list method returns a list!
148
 
        if isinstance(obj.list(), types.ListType):
149
 
            return obj.list()
150
 
        elif isinstance(obj.list(), types.GeneratorType):
151
 
            return list(obj.list())
152
 
        else:
153
 
            u.log.debug('unhandled object type: {}'.format(type(obj.list())))
154
 
            return False
155
 
 
156
 
    def delete_all_objs(self, obj, item_desc='object', max_wait=60):
157
 
        '''Delete all objects from openstack component, such as all volumes,
158
 
           all images or all snapshots.  Waits and confirms deletion.'''
159
 
        # NOTE(beisner): need to move to charmhelpers, and adjust calls here.
160
 
        # Probably useful on other charm tests.
161
 
 
162
 
        # Get list of objects to delete
163
 
        obj_list = self.force_list(obj)
164
 
        if obj_list is False:
165
 
            return '{} list failed'.format(item_desc)
166
 
 
167
 
        if len(obj_list) == 0:
168
 
            u.log.debug('no {}(s) to delete'.format(item_desc))
169
 
            return None
170
 
 
171
 
        # Delete objects
172
 
        for obj_this in obj_list:
173
 
            u.log.debug('deleting {}: {}'.format(item_desc, obj_this.id))
 
130
    def _extend_cinder_volume(self, vol_id, new_size=2):
 
131
        """Extend an existing cinder volume size.
 
132
 
 
133
        :param vol_id: existing cinder volume to extend
 
134
        :param new_size: new size in gigabytes
 
135
        :returns: None if successful; Failure message otherwise
 
136
        """
 
137
        # Extend existing volume size
 
138
        try:
 
139
            self.cinder.volumes.extend(vol_id, new_size)
 
140
            vol_size_org = self.cinder.volumes.get(vol_id).size
 
141
        except Exception as e:
 
142
            msg = 'Failed to extend volume: {}'.format(e)
 
143
            amulet.raise_status(amulet.FAIL, msg=msg)
 
144
 
 
145
        # Confirm that the volume reaches available status.
 
146
        ret = u.resource_reaches_status(self.cinder.volumes, vol_id,
 
147
                                        expected_stat="available",
 
148
                                        msg="Volume status wait")
 
149
        if not ret:
 
150
            msg = ('Cinder volume failed to reach expected state '
 
151
                   'while extending.')
 
152
            return ret
 
153
 
 
154
        # Validate volume size and status
 
155
        u.log.debug('Validating volume attributes...')
 
156
        vol_size_ext = self.cinder.volumes.get(vol_id).size
 
157
        vol_stat = self.cinder.volumes.get(vol_id).status
 
158
        msg_attr = ('Volume attributes - orig size:{} extended size:{} '
 
159
                    'stat:{}'.format(vol_size_org, vol_size_ext, vol_stat))
 
160
 
 
161
        if vol_size_ext > vol_size_org and vol_stat == 'available':
 
162
            u.log.debug(msg_attr)
 
163
        else:
 
164
            msg = ('Volume validation failed, {}'.format(msg_attr))
 
165
            return ret
 
166
 
 
167
        return None
 
168
 
 
169
    def _snapshot_cinder_volume(self, name='demo-snapshot', vol_id=None):
 
170
        """Create a snapshot of an existing cinder volume.
 
171
 
 
172
        :param name: display name to assign to snapshot
 
173
        :param vol_id: existing cinder volume to snapshot
 
174
        :returns: None if successful; Failure message otherwise
 
175
        """
 
176
        u.log.debug('Creating snapshot of volume ({})...'.format(vol_id))
 
177
        # Create snapshot of an existing cinder volume
 
178
        try:
 
179
            snap_new = self.cinder.volume_snapshots.create(
 
180
                volume_id=vol_id, display_name=name)
 
181
            snap_id = snap_new.id
 
182
        except Exception as e:
 
183
            msg = 'Failed to snapshot the volume: {}'.format(e)
 
184
            amulet.raise_status(amulet.FAIL, msg=msg)
 
185
 
 
186
        # Confirm that the volume reaches available status.
 
187
        ret = u.resource_reaches_status(self.cinder.volume_snapshots,
 
188
                                        snap_id,
 
189
                                        expected_stat="available",
 
190
                                        msg="Volume status wait")
 
191
        if not ret:
 
192
            msg = ('Cinder volume failed to reach expected state '
 
193
                   'while snapshotting.')
 
194
            return ret
 
195
 
 
196
        # Validate snapshot
 
197
        u.log.debug('Validating snapshot attributes...')
 
198
        snap_name = self.cinder.volume_snapshots.get(snap_id).display_name
 
199
        snap_stat = self.cinder.volume_snapshots.get(snap_id).status
 
200
        snap_vol_id = self.cinder.volume_snapshots.get(snap_id).volume_id
 
201
        msg_attr = ('Snapshot attributes - name:{} status:{} '
 
202
                    'vol_id:{}'.format(snap_name, snap_stat, snap_vol_id))
 
203
 
 
204
        if snap_name == name and snap_stat == 'available' \
 
205
                and snap_vol_id == vol_id:
 
206
            u.log.debug(msg_attr)
 
207
        else:
 
208
            msg = ('Snapshot validation failed, {}'.format(msg_attr))
 
209
            amulet.raise_status(amulet.FAIL, msg=msg)
 
210
 
 
211
        return snap_new
 
212
 
 
213
    def _check_cinder_lvm(self):
 
214
        """Inspect lvm on cinder unit, do basic validation against
 
215
        cinder volumes and snapshots that exist."""
 
216
        u.log.debug('Checking cinder volumes against lvm volumes...')
 
217
        # Inspect
 
218
        cmd = 'sudo lvs | grep cinder-volumes | awk \'{ print $1 }\''
 
219
        output, code = self.cinder_sentry.run(cmd)
 
220
        u.log.debug('{} `{}` returned '
 
221
                    '{}'.format(self.cinder_sentry.info['unit_name'],
 
222
                                cmd, code))
 
223
        if code != 0:
 
224
            return "command `{}` returned {}".format(cmd, str(code))
 
225
 
 
226
        vol_list = self.cinder.volumes.list()
 
227
        lv_id_list = output.split('\n')
 
228
        lv_count = len(lv_id_list)
 
229
        vol_count = len(vol_list)
 
230
        snap_count = len(self.cinder.volume_snapshots.list())
 
231
 
 
232
        # Expect cinder vol + snap count to match lvm log vol count
 
233
        u.log.debug('vols:{} snaps:{} lvs:{}'.format(vol_count,
 
234
                                                     snap_count,
 
235
                                                     lv_count))
 
236
        if (vol_count + snap_count) != len(lv_id_list):
 
237
            msg = ('lvm volume count ({}) != cinder volume + snap count '
 
238
                   '({})'.format(len(vol_list), len(lv_id_list)))
 
239
            return msg
 
240
 
 
241
        # Expect all cinder vol IDs to exist in the LVM volume list
 
242
        for vol_this in vol_list:
174
243
            try:
175
 
                obj_this.delete()
 
244
                vol_id = vol_this.id
 
245
                vol_name = vol_this.display_name
 
246
                lv_id = 'volume-{}'.format(vol_id)
 
247
                _index = lv_id_list.index(lv_id)
 
248
                u.log.info('Volume ({}) correlates to lv '
 
249
                           '{} ({})'.format(vol_name,
 
250
                                            _index,
 
251
                                            lv_id))
176
252
            except:
177
 
                return '{} delete failed for {} with status {}'.format(
178
 
                       item_desc, obj_this.id, obj_this.status)
179
 
 
180
 
        # Wait for objects to disappear
181
 
        obj_count = len(self.force_list(obj))
182
 
        tries = 0
183
 
        while obj_count != 0 and tries <= (max_wait/4):
184
 
            u.log.debug('{} delete wait: {} {}'.format(item_desc,
185
 
                                                       tries, obj_count))
186
 
            sleep(4)
187
 
            obj_count = len(self.force_list(obj))
188
 
            tries += 1
189
 
 
190
 
        if obj_count != 0:
191
 
            return '{}(s) not deleted, {} remain.'.format(item_desc,
192
 
                                                          obj_count)
193
 
 
194
 
    def obj_is_status(self, obj, obj_id, stat='available',
195
 
                      msg='openstack object status check', max_wait=120):
196
 
        ''''Wait for an openstack object status to be as expected.
197
 
            By default, expect an available status within 120s.  Useful
198
 
            when confirming cinder volumes, snapshots, glance images, etc.
199
 
            reach a certain state/status within a specified time.'''
200
 
        # NOTE(beisner): need to move to charmhelpers, and adjust calls here.
201
 
        # Probably useful on other charm tests.
202
 
 
203
 
        obj_stat = obj.get(obj_id).status
204
 
        tries = 0
205
 
        while obj_stat != stat and tries < (max_wait/4):
206
 
            u.log.debug(msg + ': {} [{}:{}] {}'.format(tries, obj_stat,
207
 
                                                       stat, obj_id))
208
 
            sleep(4)
209
 
            obj_stat = obj.get(obj_id).status
210
 
            tries += 1
211
 
        if obj_stat == stat:
212
 
            return True
213
 
        else:
214
 
            return False
215
 
 
216
 
    def test_services(self):
217
 
        '''Verify that the expected services are running on the
218
 
           corresponding service units.'''
219
 
        commands = {
220
 
            self.cinder_sentry: ['status cinder-api',
221
 
                                 'status cinder-scheduler',
222
 
                                 'status cinder-volume'],
223
 
            self.glance_sentry: ['status glance-registry',
224
 
                                 'status glance-api'],
225
 
            self.mysql_sentry: ['status mysql'],
226
 
            self.keystone_sentry: ['status keystone'],
227
 
            self.rabbitmq_sentry: ['sudo service rabbitmq-server status']
 
253
                u.log.error('lvs output: {}'.format(output))
 
254
                msg = ('Volume ID {} not found in '
 
255
                       'LVM volume list.'.format(vol_this.id))
 
256
                return msg
 
257
 
 
258
        return None
 
259
 
 
260
    def test_100_services(self):
 
261
        """Verify that the expected services are running on the
 
262
           corresponding service units."""
 
263
        services = {
 
264
            self.cinder_sentry: ['cinder-api',
 
265
                                 'cinder-scheduler',
 
266
                                 'cinder-volume'],
 
267
            self.glance_sentry: ['glance-registry',
 
268
                                 'glance-api'],
 
269
            self.mysql_sentry: ['mysql'],
 
270
            self.keystone_sentry: ['keystone'],
 
271
            self.rabbitmq_sentry: ['rabbitmq-server']
228
272
        }
229
 
        u.log.debug('commands: {}'.format(commands))
230
 
        ret = u.validate_services(commands)
231
 
        if ret:
232
 
            amulet.raise_status(amulet.FAIL, msg=ret)
233
 
 
234
 
    def test_service_catalog(self):
235
 
        '''Verify that the service catalog endpoint data'''
 
273
        ret = u.validate_services_by_name(services)
 
274
        if ret:
 
275
            amulet.raise_status(amulet.FAIL, msg=ret)
 
276
 
 
277
    def test_110_users(self):
 
278
        """Verify expected users."""
 
279
        u.log.debug('Checking keystone users...')
 
280
        user0 = {'name': 'cinder_cinderv2',
 
281
                 'enabled': True,
 
282
                 'tenantId': u.not_null,
 
283
                 'id': u.not_null,
 
284
                 'email': 'juju@localhost'}
 
285
        user1 = {'name': 'admin',
 
286
                 'enabled': True,
 
287
                 'tenantId': u.not_null,
 
288
                 'id': u.not_null,
 
289
                 'email': 'juju@localhost'}
 
290
        user2 = {'name': 'glance',
 
291
                 'enabled': True,
 
292
                 'tenantId': u.not_null,
 
293
                 'id': u.not_null,
 
294
                 'email': 'juju@localhost'}
 
295
        expected = [user0, user1, user2]
 
296
        actual = self.keystone.users.list()
 
297
 
 
298
        ret = u.validate_user_data(expected, actual)
 
299
        if ret:
 
300
            amulet.raise_status(amulet.FAIL, msg=ret)
 
301
 
 
302
    def test_112_service_catalog(self):
 
303
        """Verify that the service catalog endpoint data"""
 
304
        u.log.debug('Checking keystone service catalog...')
236
305
        endpoint_vol = {'adminURL': u.valid_url,
237
306
                        'region': 'RegionOne',
238
307
                        'publicURL': u.valid_url,
254
323
        if ret:
255
324
            amulet.raise_status(amulet.FAIL, msg=ret)
256
325
 
257
 
    def test_cinder_glance_image_service_relation(self):
258
 
        '''Verify the cinder:glance image-service relation data'''
 
326
    def test_114_cinder_endpoint(self):
 
327
        """Verify the cinder endpoint data."""
 
328
        u.log.debug('Checking cinder endpoint...')
 
329
        endpoints = self.keystone.endpoints.list()
 
330
        admin_port = internal_port = public_port = '8776'
 
331
        expected = {'id': u.not_null,
 
332
                    'region': 'RegionOne',
 
333
                    'adminurl': u.valid_url,
 
334
                    'internalurl': u.valid_url,
 
335
                    'publicurl': u.valid_url,
 
336
                    'service_id': u.not_null}
 
337
 
 
338
        ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
 
339
                                       public_port, expected)
 
340
        if ret:
 
341
            amulet.raise_status(amulet.FAIL,
 
342
                                msg='cinder endpoint: {}'.format(ret))
 
343
 
 
344
    def test_202_cinder_glance_image_service_relation(self):
 
345
        """Verify the cinder:glance image-service relation data"""
 
346
        u.log.debug('Checking cinder:glance image-service relation data...')
259
347
        unit = self.cinder_sentry
260
348
        relation = ['image-service', 'glance:image-service']
261
349
        expected = {'private-address': u.valid_ip}
262
 
        u.log.debug('')
263
350
        ret = u.validate_relation_data(unit, relation, expected)
264
351
        if ret:
265
352
            msg = u.relation_error('cinder image-service', ret)
266
353
            amulet.raise_status(amulet.FAIL, msg=msg)
267
354
 
268
 
    def test_glance_cinder_image_service_relation(self):
269
 
        '''Verify the glance:cinder image-service relation data'''
 
355
    def test_203_glance_cinder_image_service_relation(self):
 
356
        """Verify the glance:cinder image-service relation data"""
 
357
        u.log.debug('Checking glance:cinder image-service relation data...')
270
358
        unit = self.glance_sentry
271
359
        relation = ['image-service', 'cinder:image-service']
272
360
        expected = {
273
361
            'private-address': u.valid_ip,
274
362
            'glance-api-server': u.valid_url
275
363
        }
276
 
        u.log.debug('')
277
364
        ret = u.validate_relation_data(unit, relation, expected)
278
365
        if ret:
279
366
            msg = u.relation_error('glance image-service', ret)
280
367
            amulet.raise_status(amulet.FAIL, msg=msg)
281
368
 
282
 
    def test_mysql_cinder_db_relation(self):
283
 
        '''Verify the mysql:glance shared-db relation data'''
 
369
    def test_204_mysql_cinder_db_relation(self):
 
370
        """Verify the mysql:glance shared-db relation data"""
 
371
        u.log.debug('Checking mysql:cinder db relation data...')
284
372
        unit = self.mysql_sentry
285
373
        relation = ['shared-db', 'cinder:shared-db']
286
374
        expected = {
287
375
            'private-address': u.valid_ip,
288
376
            'db_host': u.valid_ip
289
377
        }
290
 
        u.log.debug('')
291
378
        ret = u.validate_relation_data(unit, relation, expected)
292
379
        if ret:
293
380
            msg = u.relation_error('mysql shared-db', ret)
294
381
            amulet.raise_status(amulet.FAIL, msg=msg)
295
382
 
296
 
    def test_cinder_mysql_db_relation(self):
297
 
        '''Verify the cinder:mysql shared-db relation data'''
 
383
    def test_205_cinder_mysql_db_relation(self):
 
384
        """Verify the cinder:mysql shared-db relation data"""
 
385
        u.log.debug('Checking cinder:mysql db relation data...')
298
386
        unit = self.cinder_sentry
299
387
        relation = ['shared-db', 'mysql:shared-db']
300
388
        expected = {
303
391
            'username': 'cinder',
304
392
            'database': 'cinder'
305
393
        }
306
 
        u.log.debug('')
307
394
        ret = u.validate_relation_data(unit, relation, expected)
308
395
        if ret:
309
396
            msg = u.relation_error('cinder shared-db', ret)
310
397
            amulet.raise_status(amulet.FAIL, msg=msg)
311
398
 
312
 
    def test_keystone_cinder_id_relation(self):
313
 
        '''Verify the keystone:cinder identity-service relation data'''
 
399
    def test_206_keystone_cinder_id_relation(self):
 
400
        """Verify the keystone:cinder identity-service relation data"""
 
401
        u.log.debug('Checking keystone:cinder id relation data...')
314
402
        unit = self.keystone_sentry
315
403
        relation = ['identity-service',
316
404
                    'cinder:identity-service']
328
416
            'service_tenant_id': u.not_null,
329
417
            'service_host': u.valid_ip
330
418
        }
331
 
        u.log.debug('')
332
419
        ret = u.validate_relation_data(unit, relation, expected)
333
420
        if ret:
334
421
            msg = u.relation_error('identity-service cinder', ret)
335
422
            amulet.raise_status(amulet.FAIL, msg=msg)
336
423
 
337
 
    def test_cinder_keystone_id_relation(self):
338
 
        '''Verify the cinder:keystone identity-service relation data'''
 
424
    def test_207_cinder_keystone_id_relation(self):
 
425
        """Verify the cinder:keystone identity-service relation data"""
 
426
        u.log.debug('Checking cinder:keystone id relation data...')
339
427
        unit = self.cinder_sentry
340
428
        relation = ['identity-service',
341
429
                    'keystone:identity-service']
347
435
            'cinder_admin_url': u.valid_url,
348
436
            'private-address': u.valid_ip
349
437
        }
350
 
        u.log.debug('')
351
438
        ret = u.validate_relation_data(unit, relation, expected)
352
439
        if ret:
353
440
            msg = u.relation_error('cinder identity-service', ret)
354
441
            amulet.raise_status(amulet.FAIL, msg=msg)
355
442
 
356
 
    def test_rabbitmq_cinder_amqp_relation(self):
357
 
        '''Verify the rabbitmq-server:cinder amqp relation data'''
 
443
    def test_208_rabbitmq_cinder_amqp_relation(self):
 
444
        """Verify the rabbitmq-server:cinder amqp relation data"""
 
445
        u.log.debug('Checking rmq:cinder amqp relation data...')
358
446
        unit = self.rabbitmq_sentry
359
447
        relation = ['amqp', 'cinder:amqp']
360
448
        expected = {
362
450
            'password': u.not_null,
363
451
            'hostname': u.valid_ip
364
452
        }
365
 
        u.log.debug('')
366
453
        ret = u.validate_relation_data(unit, relation, expected)
367
454
        if ret:
368
455
            msg = u.relation_error('amqp cinder', ret)
369
456
            amulet.raise_status(amulet.FAIL, msg=msg)
370
457
 
371
 
    def test_cinder_rabbitmq_amqp_relation(self):
372
 
        '''Verify the cinder:rabbitmq-server amqp relation data'''
 
458
    def test_209_cinder_rabbitmq_amqp_relation(self):
 
459
        """Verify the cinder:rabbitmq-server amqp relation data"""
 
460
        u.log.debug('Checking cinder:rmq amqp relation data...')
373
461
        unit = self.cinder_sentry
374
462
        relation = ['amqp', 'rabbitmq-server:amqp']
375
463
        expected = {
377
465
            'vhost': 'openstack',
378
466
            'username': u.not_null
379
467
        }
380
 
        u.log.debug('')
381
468
        ret = u.validate_relation_data(unit, relation, expected)
382
469
        if ret:
383
470
            msg = u.relation_error('cinder amqp', ret)
384
471
            amulet.raise_status(amulet.FAIL, msg=msg)
385
472
 
386
 
    def test_cinder_default_config(self):
387
 
        '''Verify default section configs in cinder.conf and
388
 
           compare some of the parameters to relation data.'''
389
 
        unit_ci = self.cinder_sentry
 
473
    def test_300_cinder_config(self):
 
474
        """Verify the data in the cinder.conf file."""
 
475
        u.log.debug('Checking cinder config file data...')
 
476
        unit = self.cinder_sentry
 
477
        conf = '/etc/cinder/cinder.conf'
390
478
        unit_mq = self.rabbitmq_sentry
391
 
        rel_ci_mq = unit_ci.relation('amqp', 'rabbitmq-server:amqp')
 
479
        unit_ks = self.keystone_sentry
392
480
        rel_mq_ci = unit_mq.relation('amqp', 'cinder:amqp')
393
 
        u.log.debug('actual ci:mq relation: {}'.format(rel_ci_mq))
394
 
        u.log.debug('actual mq:ci relation: {}'.format(rel_mq_ci))
395
 
        conf = '/etc/cinder/cinder.conf'
396
 
        expected = {'use_syslog': 'False',
397
 
                    'debug': 'False',
398
 
                    'verbose': 'False',
399
 
                    'iscsi_helper': 'tgtadm',
400
 
                    'volume_group': 'cinder-volumes',
401
 
                    'rabbit_userid': 'cinder',
402
 
                    'rabbit_password': rel_mq_ci['password'],
403
 
                    'rabbit_host': rel_mq_ci['hostname'],
404
 
                    'auth_strategy': 'keystone',
405
 
                    'volumes_dir': '/var/lib/cinder/volumes'}
406
 
        section = 'DEFAULT'
407
 
        u.log.debug('')
408
 
        ret = u.validate_config_data(unit_ci, conf, section, expected)
409
 
        if ret:
410
 
            msg = 'cinder.conf default config error: {}'.format(ret)
411
 
            amulet.raise_status(amulet.FAIL, msg=msg)
412
 
 
413
 
    def test_cinder_auth_config(self):
414
 
        '''Verify authtoken section config in cinder.conf or
415
 
           api-paste.ini using glance/keystone relation data.'''
416
 
        unit_ci = self.cinder_sentry
417
 
        unit_ks = self.keystone_sentry
418
481
        rel_ks_ci = unit_ks.relation('identity-service',
419
482
                                     'cinder:identity-service')
420
 
        u.log.debug('actual ks:ci relation: {}'.format(rel_ks_ci))
421
 
 
422
 
        expected = {'admin_user': rel_ks_ci['service_username'],
423
 
                    'admin_password': rel_ks_ci['service_password'],
424
 
                    'admin_tenant_name': rel_ks_ci['service_tenant'],
425
 
                    'auth_host': rel_ks_ci['auth_host']}
426
 
 
427
 
        if self._get_openstack_release() >= self.precise_icehouse:
428
 
            conf = '/etc/cinder/cinder.conf'
429
 
            section = 'keystone_authtoken'
430
 
            auth_uri = 'http://' + rel_ks_ci['auth_host'] + \
431
 
                       ':' + rel_ks_ci['service_port'] + '/'
432
 
            expected['auth_uri'] = auth_uri
 
483
 
 
484
        auth_uri = 'http://' + rel_ks_ci['auth_host'] + \
 
485
                   ':' + rel_ks_ci['service_port'] + '/'
 
486
 
 
487
        expected = {
 
488
            'DEFAULT': {
 
489
                'use_syslog': 'False',
 
490
                'debug': 'False',
 
491
                'verbose': 'False',
 
492
                'iscsi_helper': 'tgtadm',
 
493
                'volume_group': 'cinder-volumes',
 
494
                'auth_strategy': 'keystone',
 
495
                'volumes_dir': '/var/lib/cinder/volumes'
 
496
            },
 
497
            'keystone_authtoken': {
 
498
                'admin_user': rel_ks_ci['service_username'],
 
499
                'admin_password': rel_ks_ci['service_password'],
 
500
                'admin_tenant_name': rel_ks_ci['service_tenant'],
 
501
                'auth_uri': auth_uri
 
502
            }
 
503
        }
 
504
 
 
505
        expected_rmq = {
 
506
            'rabbit_userid': 'cinder',
 
507
            'rabbit_virtual_host': 'openstack',
 
508
            'rabbit_password': rel_mq_ci['password'],
 
509
            'rabbit_host': rel_mq_ci['hostname'],
 
510
        }
 
511
 
 
512
        if self._get_openstack_release() >= self.trusty_kilo:
 
513
            # Kilo or later
 
514
            expected['oslo_messaging_rabbit'] = expected_rmq
433
515
        else:
434
 
            conf = '/etc/cinder/api-paste.ini'
435
 
            section = 'filter:authtoken'
436
 
 
437
 
        ret = u.validate_config_data(unit_ci, conf, section, expected)
438
 
        if ret:
439
 
            msg = "cinder auth config error: {}".format(ret)
440
 
            amulet.raise_status(amulet.FAIL, msg=msg)
441
 
 
442
 
    def test_cinder_logging_config(self):
443
 
        ''' Inspect select sections and config pairs in logging.conf.'''
444
 
        unit_ci = self.cinder_sentry
 
516
            # Juno or earlier
 
517
            expected['DEFAULT'].update(expected_rmq)
 
518
            expected['keystone_authtoken']['auth_host'] = \
 
519
                rel_ks_ci['auth_host']
 
520
 
 
521
        for section, pairs in expected.iteritems():
 
522
            ret = u.validate_config_data(unit, conf, section, pairs)
 
523
            if ret:
 
524
                message = "cinder config error: {}".format(ret)
 
525
                amulet.raise_status(amulet.FAIL, msg=message)
 
526
 
 
527
    def test_301_cinder_logging_config(self):
 
528
        """Verify the data in the cinder logging conf file."""
 
529
        u.log.debug('Checking cinder logging config file data...')
 
530
        unit = self.cinder_sentry
445
531
        conf = '/etc/cinder/logging.conf'
446
532
 
447
533
        expected = {
460
546
        }
461
547
 
462
548
        for section, pairs in expected.iteritems():
463
 
            ret = u.validate_config_data(unit_ci, conf, section, pairs)
 
549
            ret = u.validate_config_data(unit, conf, section, pairs)
464
550
            if ret:
465
 
                msg = "cinder logging config error: {}".format(ret)
466
 
                amulet.raise_status(amulet.FAIL, msg=msg)
 
551
                message = "cinder logging config error: {}".format(ret)
 
552
                amulet.raise_status(amulet.FAIL, msg=message)
467
553
 
468
 
    def test_cinder_rootwrap_config(self):
469
 
        ''' Inspect select config pairs in rootwrap.conf. '''
470
 
        unit_ci = self.cinder_sentry
 
554
    def test_303_cinder_rootwrap_config(self):
 
555
        """Inspect select config pairs in rootwrap.conf."""
 
556
        u.log.debug('Checking cinder rootwrap config file data...')
 
557
        unit = self.cinder_sentry
471
558
        conf = '/etc/cinder/rootwrap.conf'
472
 
        expected = {'filters_path': '/etc/cinder/rootwrap.d,'
473
 
                                    '/usr/share/cinder/rootwrap'}
474
559
        section = 'DEFAULT'
475
 
 
476
 
        if self._get_openstack_release() >= self.precise_havana:
477
 
            expected['use_syslog'] = 'False'
478
 
            expected['exec_dirs'] = '/sbin,/usr/sbin,/bin,/usr/bin'
479
 
 
480
 
        ret = u.validate_config_data(unit_ci, conf, section, expected)
 
560
        expected = {
 
561
            'filters_path': '/etc/cinder/rootwrap.d,'
 
562
                            '/usr/share/cinder/rootwrap',
 
563
            'use_syslog': 'False',
 
564
        }
 
565
 
 
566
        ret = u.validate_config_data(unit, conf, section, expected)
481
567
        if ret:
482
568
            msg = "cinder rootwrap config error: {}".format(ret)
483
569
            amulet.raise_status(amulet.FAIL, msg=msg)
484
570
 
485
 
    def test_cinder_endpoint(self):
486
 
        '''Verify the cinder endpoint data.'''
487
 
        endpoints = self.keystone.endpoints.list()
488
 
        admin_port = internal_port = public_port = '8776'
489
 
        expected = {'id': u.not_null,
490
 
                    'region': 'RegionOne',
491
 
                    'adminurl': u.valid_url,
492
 
                    'internalurl': u.valid_url,
493
 
                    'publicurl': u.valid_url,
494
 
                    'service_id': u.not_null}
495
 
 
496
 
        ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
497
 
                                       public_port, expected)
498
 
        if ret:
499
 
            amulet.raise_status(amulet.FAIL,
500
 
                                msg='glance endpoint: {}'.format(ret))
501
 
 
502
 
    def test_z_cinder_restart_on_config_change(self):
503
 
        '''Verify cinder services are restarted when the config is changed.
504
 
 
505
 
           Note(coreycb): The method name with the _z_ is a little odd
506
 
           but it forces the test to run last.  It just makes things
507
 
           easier because restarting services requires re-authorization.
508
 
           '''
509
 
        u.log.debug('making charm config change')
510
 
        mtime = u.get_sentry_time(self.cinder_sentry)
511
 
        self.d.configure('cinder', {'verbose': 'True', 'debug': 'True'})
512
 
        if not u.validate_service_config_changed(self.cinder_sentry,
513
 
                                                 mtime,
514
 
                                                 'cinder-api',
515
 
                                                 '/etc/cinder/cinder.conf'):
516
 
            self.d.configure('cinder', {'verbose': 'False', 'debug': 'False'})
517
 
            msg = "cinder-api service didn't restart after config change"
518
 
            amulet.raise_status(amulet.FAIL, msg=msg)
519
 
 
520
 
        if not u.validate_service_config_changed(self.cinder_sentry,
521
 
                                                 mtime,
522
 
                                                 'cinder-volume',
523
 
                                                 '/etc/cinder/cinder.conf',
524
 
                                                 sleep_time=0):
525
 
            self.d.configure('cinder', {'verbose': 'False', 'debug': 'False'})
526
 
            msg = "cinder-volume service didn't restart after conf change"
527
 
            amulet.raise_status(amulet.FAIL, msg=msg)
528
 
 
529
 
        u.log.debug('returning to original charm config')
530
 
        self.d.configure('cinder', {'verbose': 'False', 'debug': 'False'})
531
 
 
532
 
    def test_users(self):
533
 
        '''Verify expected users.'''
534
 
        user0 = {'name': 'cinder_cinderv2',
535
 
                 'enabled': True,
536
 
                 'tenantId': u.not_null,
537
 
                 'id': u.not_null,
538
 
                 'email': 'juju@localhost'}
539
 
        user1 = {'name': 'admin',
540
 
                 'enabled': True,
541
 
                 'tenantId': u.not_null,
542
 
                 'id': u.not_null,
543
 
                 'email': 'juju@localhost'}
544
 
        user2 = {'name': 'glance',
545
 
                 'enabled': True,
546
 
                 'tenantId': u.not_null,
547
 
                 'id': u.not_null,
548
 
                 'email': 'juju@localhost'}
549
 
        expected = [user0, user1, user2]
550
 
        actual = self.keystone.users.list()
551
 
 
552
 
        ret = u.validate_user_data(expected, actual)
553
 
        if ret:
554
 
            amulet.raise_status(amulet.FAIL, msg=ret)
555
 
 
556
 
    def test_000_delete_volumes_snapshots_images(self):
557
 
        '''Delete all volumes, snapshots and images, if they exist,
558
 
        as the first of the ordered tests.  Useful in re-run scenarios.'''
559
 
        self.test_900_delete_all_snapshots()
560
 
        self.test_900_glance_delete_all_images()
561
 
        self.test_999_delete_all_volumes()
562
 
 
563
 
    def test_100_create_and_extend_volume(self):
564
 
        '''Add and confirm a new 1GB volume. In Havana and later,
565
 
           extend that volume to 2GB.'''
566
 
        # Create new volume
567
 
        vol_new = self.cinder.volumes.create(display_name="demo-vol", size=1)
568
 
        vol_id = vol_new.id
569
 
 
570
 
        # Wait for volume status to be available
571
 
        ret = self.obj_is_status(self.cinder.volumes, obj_id=vol_id,
572
 
                                 stat='available',
573
 
                                 msg='create vol status wait')
574
 
        if not ret:
575
 
            msg = 'volume create failed'
576
 
            amulet.raise_status(amulet.FAIL, msg=msg)
577
 
 
578
 
        # NOTE(beisner): Cinder extend is supported only in Havana or later
579
 
        if self._get_openstack_release() < self.precise_havana:
580
 
            u.log.debug('Skipping volume extend due to openstack release < H')
581
 
            return
582
 
 
583
 
        # Extend volume size
584
 
        self.cinder.volumes.extend(vol_id, '2')
585
 
 
586
 
        # Wait for extend
587
 
        vol_size = self.cinder.volumes.get(vol_id).size
588
 
        tries = 0
589
 
        while vol_size != 2 and tries <= 15:
590
 
            u.log.debug('volume extend size wait: {} {}'.format(tries,
591
 
                                                                vol_id))
592
 
            sleep(4)
593
 
            vol_size = self.cinder.volumes.get(vol_id).size
594
 
            tries += 1
595
 
 
596
 
        if vol_size != 2:
597
 
            msg = 'Failed to extend volume, size is {}'.format(vol_size)
598
 
            amulet.raise_status(amulet.FAIL, msg=msg)
599
 
 
600
 
    def test_100_glance_image_create(self):
601
 
        '''Create new cirros glance image, to be referenced by
602
 
           a cinder volume create tests in Havana or later.'''
603
 
 
604
 
        # NOTE(beisner): Cinder create vol-from-img support for lvm and
605
 
        # rbd(ceph) exists only in Havana or later
606
 
        if self._get_openstack_release() < self.precise_havana:
607
 
            u.log.debug('Skipping create glance img due to openstack rel < H')
608
 
            return
609
 
 
610
 
        # Create a new image
611
 
        image_new = u.create_cirros_image(self.glance, 'cirros-image-1')
612
 
 
613
 
        # Confirm image is created and has status of 'active'
614
 
        if not image_new:
615
 
            msg = 'image create failed'
616
 
            amulet.raise_status(amulet.FAIL, msg=msg)
617
 
 
618
 
    def test_200_clone_volume(self):
619
 
        '''Create a new cinder volume, clone it to another cinder volume.'''
620
 
        # Get volume object and ID
621
 
        try:
622
 
            vol = self.cinder.volumes.find(display_name="demo-vol")
623
 
            vol_id = vol.id
624
 
            vol_size = vol.size
625
 
        except:
626
 
            msg = ('Volume (demo-vol) not found.')
627
 
            amulet.raise_status(amulet.FAIL, msg=msg)
628
 
 
629
 
        if vol.status != 'available':
630
 
            msg = ('volume status not == available: {}'.format(vol.status))
631
 
            amulet.raise_status(amulet.FAIL, msg=msg)
632
 
 
633
 
        # Create new clone volume from source volume
634
 
        vol_clone = self.cinder.volumes.create(display_name="demo-vol-clone",
635
 
                                               size=vol_size,
636
 
                                               source_volid=vol_id)
637
 
 
638
 
        ret = self.obj_is_status(self.cinder.volumes, obj_id=vol_clone.id,
639
 
                                 stat='available',
640
 
                                 msg='clone vol status wait')
641
 
        if not ret:
642
 
            msg = 'volume clone failed - from {}'.format(vol_id)
643
 
            amulet.raise_status(amulet.FAIL, msg=msg)
644
 
 
645
 
    def test_200_create_volume_from_glance_image(self):
646
 
        '''Create new volume from glance cirros image (Havana and later),
647
 
           check status and bootable flag.'''
648
 
 
649
 
        # NOTE(beisner): Cinder create vol-from-img support for lvm and
650
 
        # rbd(ceph) exists only in Havana or later
651
 
        if self._get_openstack_release() < self.precise_havana:
652
 
            u.log.debug('Skipping create vol from img, openstack rel < H')
653
 
            return
654
 
 
655
 
        # Get image object and id
656
 
        expected_img_name = 'cirros-image-1'
657
 
        img_list = list(self.glance.images.list())
658
 
        img_count = len(img_list)
659
 
 
660
 
        if img_count != 0:
661
 
            # NOTE(beisner): glance api has no find method, presume 1st image
662
 
            img_id = img_list[0].id
663
 
        else:
664
 
            msg = 'image not found'
665
 
            amulet.raise_status(amulet.FAIL, msg=msg)
666
 
 
667
 
        # Confirm image name
668
 
        if img_list[0].name != expected_img_name:
669
 
            msg = 'unexpected image name {}'.format(img_list[0].name)
670
 
            amulet.raise_status(amulet.FAIL, msg=msg)
671
 
 
672
 
        # Create new volume from glance image
673
 
        vol_new = self.cinder.volumes.create(display_name="demo-vol-cirros",
674
 
                                             size=1, imageRef=img_id)
675
 
        vol_id = vol_new.id
676
 
 
677
 
        # Wait for volume stat to be avail, check that it's flagged bootable
678
 
        ret = self.obj_is_status(self.cinder.volumes, obj_id=vol_id,
679
 
                                 stat='available',
680
 
                                 msg='create vol from img status wait')
681
 
        vol_boot = self.cinder.volumes.get(vol_id).bootable
682
 
 
683
 
        if not ret or vol_boot != 'true':
684
 
            vol_stat = self.cinder.volumes.get(vol_id).status
685
 
            msg = ('vol create failed - from glance img:'
686
 
                   ' id:{} stat:{} boot:{}'.format(vol_id,
687
 
                                                   vol_stat,
688
 
                                                   vol_boot))
689
 
            amulet.raise_status(amulet.FAIL, msg=msg)
690
 
 
691
 
    def test_300_cinder_create_snapshot(self):
692
 
        '''Create a snapshot of a volume. Use a cirros-based volume where
693
 
           supported (Havana and newer), and fall back to a vanilla
694
 
           volume snapshot everywhere else.'''
695
 
 
696
 
        if self._get_openstack_release() >= self.precise_havana:
697
 
            vol_src_name = "demo-vol-cirros"
698
 
        elif self._get_openstack_release() < self.precise_havana:
699
 
            vol_src_name = "demo-vol"
700
 
 
701
 
        u.log.debug('creating snapshot of volume: {}'.format(vol_src_name))
702
 
 
703
 
        # Get volume object and id
704
 
        try:
705
 
            vol_src = self.cinder.volumes.find(display_name=vol_src_name)
706
 
            vol_id = vol_src.id
707
 
        except:
708
 
            msg = ('volume not found while creating snapshot')
709
 
            amulet.raise_status(amulet.FAIL, msg=msg)
710
 
 
711
 
        if vol_src.status != 'available':
712
 
            msg = ('volume status not == available: {}').format(vol_src.status)
713
 
            amulet.raise_status(amulet.FAIL, msg=msg)
714
 
 
715
 
        # Create new snapshot
716
 
        snap_new = self.cinder.volume_snapshots.create(
717
 
            volume_id=vol_id, display_name='demo-snapshot')
718
 
        snap_id = snap_new.id
719
 
 
720
 
        # Wait for snapshot status to become available
721
 
        ret = self.obj_is_status(self.cinder.volume_snapshots, obj_id=snap_id,
722
 
                                 stat='available',
723
 
                                 msg='snapshot create status wait')
724
 
        if not ret:
725
 
            snap_stat = self.cinder.volume_snapshots.get(snap_id).status
726
 
            msg = 'volume snapshot failed: {} {}'.format(snap_id,
727
 
                                                         snap_stat)
728
 
            amulet.raise_status(amulet.FAIL, msg=msg)
729
 
 
730
 
    def test_310_create_volume_from_snapshot(self):
731
 
        '''Create a new volume from a snapshot of a volume.'''
732
 
        # Get snapshot object and ID
733
 
        try:
734
 
            snap = self.cinder.volume_snapshots.find(
735
 
                display_name="demo-snapshot")
736
 
            snap_id = snap.id
737
 
            snap_size = snap.size
738
 
        except:
739
 
            msg = 'snapshot not found while creating volume'
740
 
            amulet.raise_status(amulet.FAIL, msg=msg)
741
 
 
742
 
        if snap.status != 'available':
743
 
            msg = 'snapshot status not == available: {}'.format(snap.status)
744
 
            amulet.raise_status(amulet.FAIL, msg=msg)
745
 
 
746
 
        # Create new volume from snapshot
747
 
        vol_new = self.cinder.volumes.create(
748
 
            display_name="demo-vol-from-snap",
749
 
            snapshot_id=snap_id,
750
 
            size=snap_size)
751
 
        vol_id = vol_new.id
752
 
 
753
 
        # Wait for volume status to be == available
754
 
        ret = self.obj_is_status(self.cinder.volumes, obj_id=vol_id,
755
 
                                 stat='available',
756
 
                                 msg='vol from snap create status wait')
757
 
        if not ret:
758
 
            vol_stat = self.cinder.volumes.get(vol_id).status
759
 
            msg = 'volume create failed: {} {}'.format(vol_id,
760
 
                                                       vol_stat)
761
 
            amulet.raise_status(amulet.FAIL, msg=msg)
762
 
 
763
 
    def test_900_confirm_lvm_volume_list(self):
764
 
        '''Confirm cinder volume IDs with lvm logical volume IDs.
765
 
           Expect a 1:1 relationship of lvm:cinder volumes.'''
766
 
        commando = self.cinder_sentry.run('sudo lvs | grep cinder-volumes | '
767
 
                                          'awk \'{ print $1 }\'')
768
 
        vol_list = self.cinder.volumes.list()
769
 
        lv_id_list = commando[0].split('\n')
770
 
        vol_count = len(vol_list)
771
 
        snap_count = len(self.cinder.volume_snapshots.list())
772
 
 
773
 
        # Expect cinder vol + snap count to match lvm log vol count
774
 
        if (vol_count + snap_count) != len(lv_id_list):
775
 
            msg = ('lvm volume count ({}) != cinder volume + snap count '
776
 
                   '({})'.format(len(vol_list), len(lv_id_list)))
777
 
            amulet.raise_status(amulet.FAIL, msg=msg)
778
 
 
779
 
        # Expect all cinder vol IDs to exist in the LVM volume list
780
 
        for vol_this in vol_list:
781
 
            try:
782
 
                lv_id_list.index('volume-' + vol_this.id)
783
 
            except:
784
 
                msg = ('volume ID {} not found in '
785
 
                       'LVM volume list.'.format(vol_this.id))
 
571
    def test_400_cinder_api_connection(self):
 
572
        """Simple api call to check service is up and responding"""
 
573
        u.log.debug('Checking basic cinder api functionality...')
 
574
        check = list(self.cinder.volumes.list())
 
575
        u.log.debug('Cinder api check (volumes.list): {}'.format(check))
 
576
        assert(check == [])
 
577
 
 
578
    def test_401_create_delete_volume(self):
 
579
        """Create a cinder volume and delete it."""
 
580
        u.log.debug('Creating, checking and deleting cinder volume...')
 
581
        vol_new = u.create_cinder_volume(self.cinder)
 
582
        vol_id = vol_new.id
 
583
        u.delete_resource(self.cinder.volumes, vol_id, msg="cinder volume")
 
584
 
 
585
    def test_402_create_delete_volume_from_image(self):
 
586
        """Create a cinder volume from a glance image, and delete it."""
 
587
        u.log.debug('Creating, checking and deleting cinder volume'
 
588
                    'from glance image...')
 
589
        img_new = u.create_cirros_image(self.glance, "cirros-image-1")
 
590
        img_id = img_new.id
 
591
        vol_new = u.create_cinder_volume(self.cinder,
 
592
                                         vol_name="demo-vol-cirros",
 
593
                                         img_id=img_id)
 
594
        vol_id = vol_new.id
 
595
        u.delete_resource(self.glance.images, img_id, msg="glance image")
 
596
        u.delete_resource(self.cinder.volumes, vol_id, msg="cinder volume")
 
597
 
 
598
    def test_403_volume_snap_clone_extend_inspect(self):
 
599
        """Create a cinder volume, clone it, extend its size, create a
 
600
        snapshot of the volume, create a volume from a snapshot, check
 
601
        status of each, inspect underlying lvm, then delete the resources."""
 
602
        u.log.debug('Creating, snapshotting, cloning, extending a '
 
603
                    'cinder volume...')
 
604
        vols = []
 
605
 
 
606
        # Create a 1GB volume
 
607
        vol_new = u.create_cinder_volume(self.cinder, vol_size=1)
 
608
        vols.append(vol_new)
 
609
        vol_id = vol_new.id
 
610
 
 
611
        # Snapshot the volume
 
612
        snap = self._snapshot_cinder_volume(vol_id=vol_id)
 
613
        snap_id = snap.id
 
614
 
 
615
        # Create a volume from the snapshot
 
616
        vol_from_snap = u.create_cinder_volume(self.cinder,
 
617
                                               vol_name="demo-vol-from-snap",
 
618
                                               snap_id=snap_id)
 
619
        vols.append(vol_from_snap)
 
620
 
 
621
        # Clone an existing volume
 
622
        vol_clone = u.create_cinder_volume(self.cinder,
 
623
                                           vol_name="demo-vol-clone",
 
624
                                           src_vol_id=vol_id)
 
625
        vols.append(vol_clone)
 
626
        vol_clone_id = vol_clone.id
 
627
 
 
628
        # Extend the cloned volume and confirm new size
 
629
        ret = self._extend_cinder_volume(vol_clone_id, new_size=2)
 
630
        if ret:
 
631
            amulet.raise_status(amulet.FAIL, msg=ret)
 
632
 
 
633
        # Inspect logical volumes (lvm) on cinder unit
 
634
        ret = self._check_cinder_lvm()
 
635
        if ret:
 
636
            amulet.raise_status(amulet.FAIL, msg=ret)
 
637
 
 
638
        # Cleanup
 
639
        u.log.debug('Deleting snapshot {}...'.format(snap_id))
 
640
        u.delete_resource(self.cinder.volume_snapshots,
 
641
                          snap_id, msg="cinder volume")
 
642
 
 
643
        for vol in vols:
 
644
            u.log.debug('Deleting volume {}...'.format(vol.id))
 
645
            u.delete_resource(self.cinder.volumes, vol.id, msg="cinder volume")
 
646
 
 
647
    def test_900_restart_on_config_change(self):
 
648
        """Verify that the specified services are restarted when the
 
649
        config is changed."""
 
650
 
 
651
        sentry = self.cinder_sentry
 
652
        juju_service = 'cinder'
 
653
 
 
654
        # Expected default and alternate values
 
655
        set_default = {'debug': 'False'}
 
656
        set_alternate = {'debug': 'True'}
 
657
 
 
658
        # Config file affected by juju set config change
 
659
        conf_file = '/etc/cinder/cinder.conf'
 
660
 
 
661
        # Services which are expected to restart upon config change
 
662
        services = [
 
663
            'cinder-api',
 
664
            'cinder-scheduler',
 
665
            'cinder-volume'
 
666
        ]
 
667
 
 
668
        # Make config change, check for service restarts
 
669
        u.log.debug('Making config change on {}...'.format(juju_service))
 
670
        self.d.configure(juju_service, set_alternate)
 
671
 
 
672
        sleep_time = 40
 
673
        for s in services:
 
674
            u.log.debug("Checking that service restarted: {}".format(s))
 
675
            if not u.service_restarted(sentry, s,
 
676
                                       conf_file, sleep_time=sleep_time,
 
677
                                       pgrep_full=True):
 
678
                self.d.configure(juju_service, set_default)
 
679
                msg = "service {} didn't restart after config change".format(s)
786
680
                amulet.raise_status(amulet.FAIL, msg=msg)
787
 
 
788
 
    def test_900_glance_delete_all_images(self):
789
 
        '''Delete all glance images and confirm deletion.'''
790
 
        ret = self.delete_all_objs(self.glance.images, item_desc='image')
791
 
        if ret:
792
 
            amulet.raise_status(amulet.FAIL, msg=ret)
793
 
 
794
 
    def test_900_delete_all_snapshots(self):
795
 
        '''Delete all cinder volume snapshots and confirm deletion.'''
796
 
        ret = self.delete_all_objs(self.cinder.volume_snapshots,
797
 
                                   item_desc='snapshot')
798
 
        if ret:
799
 
            amulet.raise_status(amulet.FAIL, msg=ret)
800
 
 
801
 
    def test_999_delete_all_volumes(self):
802
 
        '''Delete all cinder volumes and confirm deletion,
803
 
           as the last of the ordered tests.'''
804
 
        ret = self.delete_all_objs(self.cinder.volumes, item_desc='volume')
805
 
        if ret:
806
 
            amulet.raise_status(amulet.FAIL, msg=ret)
 
681
            sleep_time = 0
 
682
 
 
683
        self.d.configure(juju_service, set_default)