102
97
super(CinderBasicDeployment, self)._configure_services(configs)
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()))
112
# Let things settle a bit original moving forward
113
115
# Authenticate admin with keystone
114
116
self.keystone = u.authenticate_keystone_admin(self.keystone_sentry,
116
118
password='openstack',
118
121
# Authenticate admin with cinder endpoint
119
self.cinder = self.authenticate_cinder_admin(username='admin',
120
password='openstack',
122
self.cinder = u.authenticate_cinder_admin(self.keystone_sentry,
124
password='openstack',
122
127
# Authenticate admin with glance endpoint
123
128
self.glance = u.authenticate_glance_admin(self.keystone)
125
u.log.debug('openstack rel: {}'.format(self._get_openstack_release()))
126
# Wait for relations to settle
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.
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)
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.
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):
150
elif isinstance(obj.list(), types.GeneratorType):
151
return list(obj.list())
153
u.log.debug('unhandled object type: {}'.format(type(obj.list())))
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.
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)
167
if len(obj_list) == 0:
168
u.log.debug('no {}(s) to delete'.format(item_desc))
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.
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
137
# Extend existing volume size
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)
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")
150
msg = ('Cinder volume failed to reach expected state '
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))
161
if vol_size_ext > vol_size_org and vol_stat == 'available':
162
u.log.debug(msg_attr)
164
msg = ('Volume validation failed, {}'.format(msg_attr))
169
def _snapshot_cinder_volume(self, name='demo-snapshot', vol_id=None):
170
"""Create a snapshot of an existing cinder volume.
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
176
u.log.debug('Creating snapshot of volume ({})...'.format(vol_id))
177
# Create snapshot of an existing cinder volume
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)
186
# Confirm that the volume reaches available status.
187
ret = u.resource_reaches_status(self.cinder.volume_snapshots,
189
expected_stat="available",
190
msg="Volume status wait")
192
msg = ('Cinder volume failed to reach expected state '
193
'while snapshotting.')
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))
204
if snap_name == name and snap_stat == 'available' \
205
and snap_vol_id == vol_id:
206
u.log.debug(msg_attr)
208
msg = ('Snapshot validation failed, {}'.format(msg_attr))
209
amulet.raise_status(amulet.FAIL, msg=msg)
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...')
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'],
224
return "command `{}` returned {}".format(cmd, str(code))
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())
232
# Expect cinder vol + snap count to match lvm log vol count
233
u.log.debug('vols:{} snaps:{} lvs:{}'.format(vol_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)))
241
# Expect all cinder vol IDs to exist in the LVM volume list
242
for vol_this in vol_list:
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,
177
return '{} delete failed for {} with status {}'.format(
178
item_desc, obj_this.id, obj_this.status)
180
# Wait for objects to disappear
181
obj_count = len(self.force_list(obj))
183
while obj_count != 0 and tries <= (max_wait/4):
184
u.log.debug('{} delete wait: {} {}'.format(item_desc,
187
obj_count = len(self.force_list(obj))
191
return '{}(s) not deleted, {} remain.'.format(item_desc,
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.
203
obj_stat = obj.get(obj_id).status
205
while obj_stat != stat and tries < (max_wait/4):
206
u.log.debug(msg + ': {} [{}:{}] {}'.format(tries, obj_stat,
209
obj_stat = obj.get(obj_id).status
216
def test_services(self):
217
'''Verify that the expected services are running on the
218
corresponding service units.'''
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))
260
def test_100_services(self):
261
"""Verify that the expected services are running on the
262
corresponding service units."""
264
self.cinder_sentry: ['cinder-api',
267
self.glance_sentry: ['glance-registry',
269
self.mysql_sentry: ['mysql'],
270
self.keystone_sentry: ['keystone'],
271
self.rabbitmq_sentry: ['rabbitmq-server']
229
u.log.debug('commands: {}'.format(commands))
230
ret = u.validate_services(commands)
232
amulet.raise_status(amulet.FAIL, msg=ret)
234
def test_service_catalog(self):
235
'''Verify that the service catalog endpoint data'''
273
ret = u.validate_services_by_name(services)
275
amulet.raise_status(amulet.FAIL, msg=ret)
277
def test_110_users(self):
278
"""Verify expected users."""
279
u.log.debug('Checking keystone users...')
280
user0 = {'name': 'cinder_cinderv2',
282
'tenantId': u.not_null,
284
'email': 'juju@localhost'}
285
user1 = {'name': 'admin',
287
'tenantId': u.not_null,
289
'email': 'juju@localhost'}
290
user2 = {'name': 'glance',
292
'tenantId': u.not_null,
294
'email': 'juju@localhost'}
295
expected = [user0, user1, user2]
296
actual = self.keystone.users.list()
298
ret = u.validate_user_data(expected, actual)
300
amulet.raise_status(amulet.FAIL, msg=ret)
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,
377
465
'vhost': 'openstack',
378
466
'username': u.not_null
381
468
ret = u.validate_relation_data(unit, relation, expected)
383
470
msg = u.relation_error('cinder amqp', ret)
384
471
amulet.raise_status(amulet.FAIL, msg=msg)
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',
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'}
408
ret = u.validate_config_data(unit_ci, conf, section, expected)
410
msg = 'cinder.conf default config error: {}'.format(ret)
411
amulet.raise_status(amulet.FAIL, msg=msg)
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))
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']}
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
484
auth_uri = 'http://' + rel_ks_ci['auth_host'] + \
485
':' + rel_ks_ci['service_port'] + '/'
489
'use_syslog': 'False',
492
'iscsi_helper': 'tgtadm',
493
'volume_group': 'cinder-volumes',
494
'auth_strategy': 'keystone',
495
'volumes_dir': '/var/lib/cinder/volumes'
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'],
506
'rabbit_userid': 'cinder',
507
'rabbit_virtual_host': 'openstack',
508
'rabbit_password': rel_mq_ci['password'],
509
'rabbit_host': rel_mq_ci['hostname'],
512
if self._get_openstack_release() >= self.trusty_kilo:
514
expected['oslo_messaging_rabbit'] = expected_rmq
434
conf = '/etc/cinder/api-paste.ini'
435
section = 'filter:authtoken'
437
ret = u.validate_config_data(unit_ci, conf, section, expected)
439
msg = "cinder auth config error: {}".format(ret)
440
amulet.raise_status(amulet.FAIL, msg=msg)
442
def test_cinder_logging_config(self):
443
''' Inspect select sections and config pairs in logging.conf.'''
444
unit_ci = self.cinder_sentry
517
expected['DEFAULT'].update(expected_rmq)
518
expected['keystone_authtoken']['auth_host'] = \
519
rel_ks_ci['auth_host']
521
for section, pairs in expected.iteritems():
522
ret = u.validate_config_data(unit, conf, section, pairs)
524
message = "cinder config error: {}".format(ret)
525
amulet.raise_status(amulet.FAIL, msg=message)
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'
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)
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)
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'
476
if self._get_openstack_release() >= self.precise_havana:
477
expected['use_syslog'] = 'False'
478
expected['exec_dirs'] = '/sbin,/usr/sbin,/bin,/usr/bin'
480
ret = u.validate_config_data(unit_ci, conf, section, expected)
561
'filters_path': '/etc/cinder/rootwrap.d,'
562
'/usr/share/cinder/rootwrap',
563
'use_syslog': 'False',
566
ret = u.validate_config_data(unit, conf, section, expected)
482
568
msg = "cinder rootwrap config error: {}".format(ret)
483
569
amulet.raise_status(amulet.FAIL, msg=msg)
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}
496
ret = u.validate_endpoint_data(endpoints, admin_port, internal_port,
497
public_port, expected)
499
amulet.raise_status(amulet.FAIL,
500
msg='glance endpoint: {}'.format(ret))
502
def test_z_cinder_restart_on_config_change(self):
503
'''Verify cinder services are restarted when the config is changed.
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.
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,
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)
520
if not u.validate_service_config_changed(self.cinder_sentry,
523
'/etc/cinder/cinder.conf',
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)
529
u.log.debug('returning to original charm config')
530
self.d.configure('cinder', {'verbose': 'False', 'debug': 'False'})
532
def test_users(self):
533
'''Verify expected users.'''
534
user0 = {'name': 'cinder_cinderv2',
536
'tenantId': u.not_null,
538
'email': 'juju@localhost'}
539
user1 = {'name': 'admin',
541
'tenantId': u.not_null,
543
'email': 'juju@localhost'}
544
user2 = {'name': 'glance',
546
'tenantId': u.not_null,
548
'email': 'juju@localhost'}
549
expected = [user0, user1, user2]
550
actual = self.keystone.users.list()
552
ret = u.validate_user_data(expected, actual)
554
amulet.raise_status(amulet.FAIL, msg=ret)
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()
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.'''
567
vol_new = self.cinder.volumes.create(display_name="demo-vol", size=1)
570
# Wait for volume status to be available
571
ret = self.obj_is_status(self.cinder.volumes, obj_id=vol_id,
573
msg='create vol status wait')
575
msg = 'volume create failed'
576
amulet.raise_status(amulet.FAIL, msg=msg)
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')
584
self.cinder.volumes.extend(vol_id, '2')
587
vol_size = self.cinder.volumes.get(vol_id).size
589
while vol_size != 2 and tries <= 15:
590
u.log.debug('volume extend size wait: {} {}'.format(tries,
593
vol_size = self.cinder.volumes.get(vol_id).size
597
msg = 'Failed to extend volume, size is {}'.format(vol_size)
598
amulet.raise_status(amulet.FAIL, msg=msg)
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.'''
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')
611
image_new = u.create_cirros_image(self.glance, 'cirros-image-1')
613
# Confirm image is created and has status of 'active'
615
msg = 'image create failed'
616
amulet.raise_status(amulet.FAIL, msg=msg)
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
622
vol = self.cinder.volumes.find(display_name="demo-vol")
626
msg = ('Volume (demo-vol) not found.')
627
amulet.raise_status(amulet.FAIL, msg=msg)
629
if vol.status != 'available':
630
msg = ('volume status not == available: {}'.format(vol.status))
631
amulet.raise_status(amulet.FAIL, msg=msg)
633
# Create new clone volume from source volume
634
vol_clone = self.cinder.volumes.create(display_name="demo-vol-clone",
638
ret = self.obj_is_status(self.cinder.volumes, obj_id=vol_clone.id,
640
msg='clone vol status wait')
642
msg = 'volume clone failed - from {}'.format(vol_id)
643
amulet.raise_status(amulet.FAIL, msg=msg)
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.'''
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')
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)
661
# NOTE(beisner): glance api has no find method, presume 1st image
662
img_id = img_list[0].id
664
msg = 'image not found'
665
amulet.raise_status(amulet.FAIL, msg=msg)
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)
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)
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,
680
msg='create vol from img status wait')
681
vol_boot = self.cinder.volumes.get(vol_id).bootable
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,
689
amulet.raise_status(amulet.FAIL, msg=msg)
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.'''
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"
701
u.log.debug('creating snapshot of volume: {}'.format(vol_src_name))
703
# Get volume object and id
705
vol_src = self.cinder.volumes.find(display_name=vol_src_name)
708
msg = ('volume not found while creating snapshot')
709
amulet.raise_status(amulet.FAIL, msg=msg)
711
if vol_src.status != 'available':
712
msg = ('volume status not == available: {}').format(vol_src.status)
713
amulet.raise_status(amulet.FAIL, msg=msg)
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
720
# Wait for snapshot status to become available
721
ret = self.obj_is_status(self.cinder.volume_snapshots, obj_id=snap_id,
723
msg='snapshot create status wait')
725
snap_stat = self.cinder.volume_snapshots.get(snap_id).status
726
msg = 'volume snapshot failed: {} {}'.format(snap_id,
728
amulet.raise_status(amulet.FAIL, msg=msg)
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
734
snap = self.cinder.volume_snapshots.find(
735
display_name="demo-snapshot")
737
snap_size = snap.size
739
msg = 'snapshot not found while creating volume'
740
amulet.raise_status(amulet.FAIL, msg=msg)
742
if snap.status != 'available':
743
msg = 'snapshot status not == available: {}'.format(snap.status)
744
amulet.raise_status(amulet.FAIL, msg=msg)
746
# Create new volume from snapshot
747
vol_new = self.cinder.volumes.create(
748
display_name="demo-vol-from-snap",
753
# Wait for volume status to be == available
754
ret = self.obj_is_status(self.cinder.volumes, obj_id=vol_id,
756
msg='vol from snap create status wait')
758
vol_stat = self.cinder.volumes.get(vol_id).status
759
msg = 'volume create failed: {} {}'.format(vol_id,
761
amulet.raise_status(amulet.FAIL, msg=msg)
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())
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)
779
# Expect all cinder vol IDs to exist in the LVM volume list
780
for vol_this in vol_list:
782
lv_id_list.index('volume-' + vol_this.id)
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))
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)
583
u.delete_resource(self.cinder.volumes, vol_id, msg="cinder volume")
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")
591
vol_new = u.create_cinder_volume(self.cinder,
592
vol_name="demo-vol-cirros",
595
u.delete_resource(self.glance.images, img_id, msg="glance image")
596
u.delete_resource(self.cinder.volumes, vol_id, msg="cinder volume")
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 '
606
# Create a 1GB volume
607
vol_new = u.create_cinder_volume(self.cinder, vol_size=1)
611
# Snapshot the volume
612
snap = self._snapshot_cinder_volume(vol_id=vol_id)
615
# Create a volume from the snapshot
616
vol_from_snap = u.create_cinder_volume(self.cinder,
617
vol_name="demo-vol-from-snap",
619
vols.append(vol_from_snap)
621
# Clone an existing volume
622
vol_clone = u.create_cinder_volume(self.cinder,
623
vol_name="demo-vol-clone",
625
vols.append(vol_clone)
626
vol_clone_id = vol_clone.id
628
# Extend the cloned volume and confirm new size
629
ret = self._extend_cinder_volume(vol_clone_id, new_size=2)
631
amulet.raise_status(amulet.FAIL, msg=ret)
633
# Inspect logical volumes (lvm) on cinder unit
634
ret = self._check_cinder_lvm()
636
amulet.raise_status(amulet.FAIL, msg=ret)
639
u.log.debug('Deleting snapshot {}...'.format(snap_id))
640
u.delete_resource(self.cinder.volume_snapshots,
641
snap_id, msg="cinder volume")
644
u.log.debug('Deleting volume {}...'.format(vol.id))
645
u.delete_resource(self.cinder.volumes, vol.id, msg="cinder volume")
647
def test_900_restart_on_config_change(self):
648
"""Verify that the specified services are restarted when the
649
config is changed."""
651
sentry = self.cinder_sentry
652
juju_service = 'cinder'
654
# Expected default and alternate values
655
set_default = {'debug': 'False'}
656
set_alternate = {'debug': 'True'}
658
# Config file affected by juju set config change
659
conf_file = '/etc/cinder/cinder.conf'
661
# Services which are expected to restart upon config change
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)
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,
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)
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')
792
amulet.raise_status(amulet.FAIL, msg=ret)
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')
799
amulet.raise_status(amulet.FAIL, msg=ret)
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')
806
amulet.raise_status(amulet.FAIL, msg=ret)
683
self.d.configure(juju_service, set_default)