This change adds the basic boot-from-volume support to the image service.
Specifically following API will supports --block-device-mapping with volume/snapshot and root device name - register image - describe image - create image(newly support) At the moment swap and ephemeral aren't supported yet. They will be supported with the next step Next step - describe instance attribute with euca command - get metadata for bundle volume - swap/ephemeral device support
This commit is contained in:
@@ -92,7 +92,9 @@ class XmlConversionTestCase(test.TestCase):
|
|||||||
conv = ec2utils._try_convert
|
conv = ec2utils._try_convert
|
||||||
self.assertEqual(conv('None'), None)
|
self.assertEqual(conv('None'), None)
|
||||||
self.assertEqual(conv('True'), True)
|
self.assertEqual(conv('True'), True)
|
||||||
|
self.assertEqual(conv('true'), True)
|
||||||
self.assertEqual(conv('False'), False)
|
self.assertEqual(conv('False'), False)
|
||||||
|
self.assertEqual(conv('false'), False)
|
||||||
self.assertEqual(conv('0'), 0)
|
self.assertEqual(conv('0'), 0)
|
||||||
self.assertEqual(conv('42'), 42)
|
self.assertEqual(conv('42'), 42)
|
||||||
self.assertEqual(conv('3.14'), 3.14)
|
self.assertEqual(conv('3.14'), 3.14)
|
||||||
@@ -107,6 +109,8 @@ class Ec2utilsTestCase(test.TestCase):
|
|||||||
def test_ec2_id_to_id(self):
|
def test_ec2_id_to_id(self):
|
||||||
self.assertEqual(ec2utils.ec2_id_to_id('i-0000001e'), 30)
|
self.assertEqual(ec2utils.ec2_id_to_id('i-0000001e'), 30)
|
||||||
self.assertEqual(ec2utils.ec2_id_to_id('ami-1d'), 29)
|
self.assertEqual(ec2utils.ec2_id_to_id('ami-1d'), 29)
|
||||||
|
self.assertEqual(ec2utils.ec2_id_to_id('snap-0000001c'), 28)
|
||||||
|
self.assertEqual(ec2utils.ec2_id_to_id('vol-0000001b'), 27)
|
||||||
|
|
||||||
def test_bad_ec2_id(self):
|
def test_bad_ec2_id(self):
|
||||||
self.assertRaises(exception.InvalidEc2Id,
|
self.assertRaises(exception.InvalidEc2Id,
|
||||||
@@ -116,6 +120,72 @@ class Ec2utilsTestCase(test.TestCase):
|
|||||||
def test_id_to_ec2_id(self):
|
def test_id_to_ec2_id(self):
|
||||||
self.assertEqual(ec2utils.id_to_ec2_id(30), 'i-0000001e')
|
self.assertEqual(ec2utils.id_to_ec2_id(30), 'i-0000001e')
|
||||||
self.assertEqual(ec2utils.id_to_ec2_id(29, 'ami-%08x'), 'ami-0000001d')
|
self.assertEqual(ec2utils.id_to_ec2_id(29, 'ami-%08x'), 'ami-0000001d')
|
||||||
|
self.assertEqual(ec2utils.id_to_ec2_snap_id(28), 'snap-0000001c')
|
||||||
|
self.assertEqual(ec2utils.id_to_ec2_vol_id(27), 'vol-0000001b')
|
||||||
|
|
||||||
|
def test_dict_from_dotted_str(self):
|
||||||
|
in_str = [('BlockDeviceMapping.1.DeviceName', '/dev/sda1'),
|
||||||
|
('BlockDeviceMapping.1.Ebs.SnapshotId', 'snap-0000001c'),
|
||||||
|
('BlockDeviceMapping.1.Ebs.VolumeSize', '80'),
|
||||||
|
('BlockDeviceMapping.1.Ebs.DeleteOnTermination', 'false'),
|
||||||
|
('BlockDeviceMapping.2.DeviceName', '/dev/sdc'),
|
||||||
|
('BlockDeviceMapping.2.VirtualName', 'ephemeral0')]
|
||||||
|
expected_dict = {
|
||||||
|
'block_device_mapping': {
|
||||||
|
'1': {'device_name': '/dev/sda1',
|
||||||
|
'ebs': {'snapshot_id': 'snap-0000001c',
|
||||||
|
'volume_size': 80,
|
||||||
|
'delete_on_termination': False}},
|
||||||
|
'2': {'device_name': '/dev/sdc',
|
||||||
|
'virtual_name': 'ephemeral0'}}}
|
||||||
|
out_dict = ec2utils.dict_from_dotted_str(in_str)
|
||||||
|
|
||||||
|
self.assertDictMatch(out_dict, expected_dict)
|
||||||
|
|
||||||
|
def test_properties_root_defice_name(self):
|
||||||
|
mappings = [{"device": "/dev/sda1", "virtual": "root"}]
|
||||||
|
properties0 = {'mappings': mappings}
|
||||||
|
properties1 = {'root_device_name': '/dev/sdb', 'mappings': mappings}
|
||||||
|
|
||||||
|
root_device_name = ec2utils.properties_root_device_name(properties0)
|
||||||
|
self.assertEqual(root_device_name, '/dev/sda1')
|
||||||
|
|
||||||
|
root_device_name = ec2utils.properties_root_device_name(properties1)
|
||||||
|
self.assertEqual(root_device_name, '/dev/sdb')
|
||||||
|
|
||||||
|
def test_mapping_prepend_dev(self):
|
||||||
|
mappings = [
|
||||||
|
{'virtual': 'ami',
|
||||||
|
'device': 'sda1'},
|
||||||
|
{'virtual': 'root',
|
||||||
|
'device': '/dev/sda1'},
|
||||||
|
|
||||||
|
{'virtual': 'swap',
|
||||||
|
'device': 'sdb1'},
|
||||||
|
{'virtual': 'swap',
|
||||||
|
'device': '/dev/sdb2'},
|
||||||
|
|
||||||
|
{'virtual': 'ephemeral0',
|
||||||
|
'device': 'sdc1'},
|
||||||
|
{'virtual': 'ephemeral1',
|
||||||
|
'device': '/dev/sdc1'}]
|
||||||
|
expected_result = [
|
||||||
|
{'virtual': 'ami',
|
||||||
|
'device': 'sda1'},
|
||||||
|
{'virtual': 'root',
|
||||||
|
'device': '/dev/sda1'},
|
||||||
|
|
||||||
|
{'virtual': 'swap',
|
||||||
|
'device': '/dev/sdb1'},
|
||||||
|
{'virtual': 'swap',
|
||||||
|
'device': '/dev/sdb2'},
|
||||||
|
|
||||||
|
{'virtual': 'ephemeral0',
|
||||||
|
'device': '/dev/sdc1'},
|
||||||
|
{'virtual': 'ephemeral1',
|
||||||
|
'device': '/dev/sdc1'}]
|
||||||
|
self.assertDictListMatch(ec2utils.mappings_prepend_dev(mappings),
|
||||||
|
expected_result)
|
||||||
|
|
||||||
|
|
||||||
class ApiEc2TestCase(test.TestCase):
|
class ApiEc2TestCase(test.TestCase):
|
||||||
|
|||||||
@@ -45,7 +45,8 @@ LOG = logging.getLogger('nova.tests.cloud')
|
|||||||
class CloudTestCase(test.TestCase):
|
class CloudTestCase(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(CloudTestCase, self).setUp()
|
super(CloudTestCase, self).setUp()
|
||||||
self.flags(connection_type='fake')
|
self.flags(connection_type='fake',
|
||||||
|
stub_network=True)
|
||||||
|
|
||||||
self.conn = rpc.Connection.instance()
|
self.conn = rpc.Connection.instance()
|
||||||
|
|
||||||
@@ -290,7 +291,7 @@ class CloudTestCase(test.TestCase):
|
|||||||
vol2 = db.volume_create(self.context, {})
|
vol2 = db.volume_create(self.context, {})
|
||||||
result = self.cloud.describe_volumes(self.context)
|
result = self.cloud.describe_volumes(self.context)
|
||||||
self.assertEqual(len(result['volumeSet']), 2)
|
self.assertEqual(len(result['volumeSet']), 2)
|
||||||
volume_id = ec2utils.id_to_ec2_id(vol2['id'], 'vol-%08x')
|
volume_id = ec2utils.id_to_ec2_vol_id(vol2['id'])
|
||||||
result = self.cloud.describe_volumes(self.context,
|
result = self.cloud.describe_volumes(self.context,
|
||||||
volume_id=[volume_id])
|
volume_id=[volume_id])
|
||||||
self.assertEqual(len(result['volumeSet']), 1)
|
self.assertEqual(len(result['volumeSet']), 1)
|
||||||
@@ -306,7 +307,7 @@ class CloudTestCase(test.TestCase):
|
|||||||
snap = db.snapshot_create(self.context, {'volume_id': vol['id'],
|
snap = db.snapshot_create(self.context, {'volume_id': vol['id'],
|
||||||
'volume_size': vol['size'],
|
'volume_size': vol['size'],
|
||||||
'status': "available"})
|
'status': "available"})
|
||||||
snapshot_id = ec2utils.id_to_ec2_id(snap['id'], 'snap-%08x')
|
snapshot_id = ec2utils.id_to_ec2_snap_id(snap['id'])
|
||||||
|
|
||||||
result = self.cloud.create_volume(self.context,
|
result = self.cloud.create_volume(self.context,
|
||||||
snapshot_id=snapshot_id)
|
snapshot_id=snapshot_id)
|
||||||
@@ -345,7 +346,7 @@ class CloudTestCase(test.TestCase):
|
|||||||
snap2 = db.snapshot_create(self.context, {'volume_id': vol['id']})
|
snap2 = db.snapshot_create(self.context, {'volume_id': vol['id']})
|
||||||
result = self.cloud.describe_snapshots(self.context)
|
result = self.cloud.describe_snapshots(self.context)
|
||||||
self.assertEqual(len(result['snapshotSet']), 2)
|
self.assertEqual(len(result['snapshotSet']), 2)
|
||||||
snapshot_id = ec2utils.id_to_ec2_id(snap2['id'], 'snap-%08x')
|
snapshot_id = ec2utils.id_to_ec2_snap_id(snap2['id'])
|
||||||
result = self.cloud.describe_snapshots(self.context,
|
result = self.cloud.describe_snapshots(self.context,
|
||||||
snapshot_id=[snapshot_id])
|
snapshot_id=[snapshot_id])
|
||||||
self.assertEqual(len(result['snapshotSet']), 1)
|
self.assertEqual(len(result['snapshotSet']), 1)
|
||||||
@@ -359,7 +360,7 @@ class CloudTestCase(test.TestCase):
|
|||||||
def test_create_snapshot(self):
|
def test_create_snapshot(self):
|
||||||
"""Makes sure create_snapshot works."""
|
"""Makes sure create_snapshot works."""
|
||||||
vol = db.volume_create(self.context, {'status': "available"})
|
vol = db.volume_create(self.context, {'status': "available"})
|
||||||
volume_id = ec2utils.id_to_ec2_id(vol['id'], 'vol-%08x')
|
volume_id = ec2utils.id_to_ec2_vol_id(vol['id'])
|
||||||
|
|
||||||
result = self.cloud.create_snapshot(self.context,
|
result = self.cloud.create_snapshot(self.context,
|
||||||
volume_id=volume_id)
|
volume_id=volume_id)
|
||||||
@@ -376,7 +377,7 @@ class CloudTestCase(test.TestCase):
|
|||||||
vol = db.volume_create(self.context, {'status': "available"})
|
vol = db.volume_create(self.context, {'status': "available"})
|
||||||
snap = db.snapshot_create(self.context, {'volume_id': vol['id'],
|
snap = db.snapshot_create(self.context, {'volume_id': vol['id'],
|
||||||
'status': "available"})
|
'status': "available"})
|
||||||
snapshot_id = ec2utils.id_to_ec2_id(snap['id'], 'snap-%08x')
|
snapshot_id = ec2utils.id_to_ec2_snap_id(snap['id'])
|
||||||
|
|
||||||
result = self.cloud.delete_snapshot(self.context,
|
result = self.cloud.delete_snapshot(self.context,
|
||||||
snapshot_id=snapshot_id)
|
snapshot_id=snapshot_id)
|
||||||
@@ -415,6 +416,185 @@ class CloudTestCase(test.TestCase):
|
|||||||
db.service_destroy(self.context, comp1['id'])
|
db.service_destroy(self.context, comp1['id'])
|
||||||
db.service_destroy(self.context, comp2['id'])
|
db.service_destroy(self.context, comp2['id'])
|
||||||
|
|
||||||
|
def _block_device_mapping_create(self, instance_id, mappings):
|
||||||
|
volumes = []
|
||||||
|
for bdm in mappings:
|
||||||
|
db.block_device_mapping_create(self.context, bdm)
|
||||||
|
if 'volume_id' in bdm:
|
||||||
|
values = {'id': bdm['volume_id']}
|
||||||
|
for bdm_key, vol_key in [('snapshot_id', 'snapshot_id'),
|
||||||
|
('snapshot_size', 'volume_size'),
|
||||||
|
('delete_on_termination',
|
||||||
|
'delete_on_termination')]:
|
||||||
|
if bdm_key in bdm:
|
||||||
|
values[vol_key] = bdm[bdm_key]
|
||||||
|
vol = db.volume_create(self.context, values)
|
||||||
|
db.volume_attached(self.context, vol['id'],
|
||||||
|
instance_id, bdm['device_name'])
|
||||||
|
volumes.append(vol)
|
||||||
|
return volumes
|
||||||
|
|
||||||
|
def _setUpBlockDeviceMapping(self):
|
||||||
|
inst1 = db.instance_create(self.context,
|
||||||
|
{'image_ref': 1,
|
||||||
|
'root_device_name': '/dev/sdb1'})
|
||||||
|
inst2 = db.instance_create(self.context,
|
||||||
|
{'image_ref': 2,
|
||||||
|
'root_device_name': '/dev/sdc1'})
|
||||||
|
|
||||||
|
instance_id = inst1['id']
|
||||||
|
mappings0 = [
|
||||||
|
{'instance_id': instance_id,
|
||||||
|
'device_name': '/dev/sdb1',
|
||||||
|
'snapshot_id': '1',
|
||||||
|
'volume_id': '2'},
|
||||||
|
{'instance_id': instance_id,
|
||||||
|
'device_name': '/dev/sdb2',
|
||||||
|
'volume_id': '3',
|
||||||
|
'volume_size': 1},
|
||||||
|
{'instance_id': instance_id,
|
||||||
|
'device_name': '/dev/sdb3',
|
||||||
|
'delete_on_termination': True,
|
||||||
|
'snapshot_id': '4',
|
||||||
|
'volume_id': '5'},
|
||||||
|
{'instance_id': instance_id,
|
||||||
|
'device_name': '/dev/sdb4',
|
||||||
|
'delete_on_termination': False,
|
||||||
|
'snapshot_id': '6',
|
||||||
|
'volume_id': '7'},
|
||||||
|
{'instance_id': instance_id,
|
||||||
|
'device_name': '/dev/sdb5',
|
||||||
|
'snapshot_id': '8',
|
||||||
|
'volume_id': '9',
|
||||||
|
'volume_size': 0},
|
||||||
|
{'instance_id': instance_id,
|
||||||
|
'device_name': '/dev/sdb6',
|
||||||
|
'snapshot_id': '10',
|
||||||
|
'volume_id': '11',
|
||||||
|
'volume_size': 1},
|
||||||
|
{'instance_id': instance_id,
|
||||||
|
'device_name': '/dev/sdb7',
|
||||||
|
'no_device': True},
|
||||||
|
{'instance_id': instance_id,
|
||||||
|
'device_name': '/dev/sdb8',
|
||||||
|
'virtual_name': 'swap'},
|
||||||
|
{'instance_id': instance_id,
|
||||||
|
'device_name': '/dev/sdb9',
|
||||||
|
'virtual_name': 'ephemeral3'}]
|
||||||
|
|
||||||
|
volumes = self._block_device_mapping_create(instance_id, mappings0)
|
||||||
|
return (inst1, inst2, volumes)
|
||||||
|
|
||||||
|
def _tearDownBlockDeviceMapping(self, inst1, inst2, volumes):
|
||||||
|
for vol in volumes:
|
||||||
|
db.volume_destroy(self.context, vol['id'])
|
||||||
|
for id in (inst1['id'], inst2['id']):
|
||||||
|
for bdm in db.block_device_mapping_get_all_by_instance(
|
||||||
|
self.context, id):
|
||||||
|
db.block_device_mapping_destroy(self.context, bdm['id'])
|
||||||
|
db.instance_destroy(self.context, inst2['id'])
|
||||||
|
db.instance_destroy(self.context, inst1['id'])
|
||||||
|
|
||||||
|
_expected_instance_bdm1 = {
|
||||||
|
'instanceId': 'i-00000001',
|
||||||
|
'rootDeviceName': '/dev/sdb1',
|
||||||
|
'rootDeviceType': 'ebs'}
|
||||||
|
|
||||||
|
_expected_block_device_mapping0 = [
|
||||||
|
{'deviceName': '/dev/sdb1',
|
||||||
|
'ebs': {'status': 'in-use',
|
||||||
|
'deleteOnTermination': False,
|
||||||
|
'volumeId': 2,
|
||||||
|
}},
|
||||||
|
{'deviceName': '/dev/sdb2',
|
||||||
|
'ebs': {'status': 'in-use',
|
||||||
|
'deleteOnTermination': False,
|
||||||
|
'volumeId': 3,
|
||||||
|
}},
|
||||||
|
{'deviceName': '/dev/sdb3',
|
||||||
|
'ebs': {'status': 'in-use',
|
||||||
|
'deleteOnTermination': True,
|
||||||
|
'volumeId': 5,
|
||||||
|
}},
|
||||||
|
{'deviceName': '/dev/sdb4',
|
||||||
|
'ebs': {'status': 'in-use',
|
||||||
|
'deleteOnTermination': False,
|
||||||
|
'volumeId': 7,
|
||||||
|
}},
|
||||||
|
{'deviceName': '/dev/sdb5',
|
||||||
|
'ebs': {'status': 'in-use',
|
||||||
|
'deleteOnTermination': False,
|
||||||
|
'volumeId': 9,
|
||||||
|
}},
|
||||||
|
{'deviceName': '/dev/sdb6',
|
||||||
|
'ebs': {'status': 'in-use',
|
||||||
|
'deleteOnTermination': False,
|
||||||
|
'volumeId': 11, }}]
|
||||||
|
# NOTE(yamahata): swap/ephemeral device case isn't supported yet.
|
||||||
|
|
||||||
|
_expected_instance_bdm2 = {
|
||||||
|
'instanceId': 'i-00000002',
|
||||||
|
'rootDeviceName': '/dev/sdc1',
|
||||||
|
'rootDeviceType': 'instance-store'}
|
||||||
|
|
||||||
|
def test_format_instance_bdm(self):
|
||||||
|
(inst1, inst2, volumes) = self._setUpBlockDeviceMapping()
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
self.cloud._format_instance_bdm(self.context, inst1['id'], '/dev/sdb1',
|
||||||
|
result)
|
||||||
|
self.assertSubDictMatch(
|
||||||
|
{'rootDeviceType': self._expected_instance_bdm1['rootDeviceType']},
|
||||||
|
result)
|
||||||
|
self._assertEqualBlockDeviceMapping(
|
||||||
|
self._expected_block_device_mapping0, result['blockDeviceMapping'])
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
self.cloud._format_instance_bdm(self.context, inst2['id'], '/dev/sdc1',
|
||||||
|
result)
|
||||||
|
self.assertSubDictMatch(
|
||||||
|
{'rootDeviceType': self._expected_instance_bdm2['rootDeviceType']},
|
||||||
|
result)
|
||||||
|
|
||||||
|
self._tearDownBlockDeviceMapping(inst1, inst2, volumes)
|
||||||
|
|
||||||
|
def _assertInstance(self, instance_id):
|
||||||
|
ec2_instance_id = ec2utils.id_to_ec2_id(instance_id)
|
||||||
|
result = self.cloud.describe_instances(self.context,
|
||||||
|
instance_id=[ec2_instance_id])
|
||||||
|
result = result['reservationSet'][0]
|
||||||
|
self.assertEqual(len(result['instancesSet']), 1)
|
||||||
|
result = result['instancesSet'][0]
|
||||||
|
self.assertEqual(result['instanceId'], ec2_instance_id)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _assertEqualBlockDeviceMapping(self, expected, result):
|
||||||
|
self.assertEqual(len(expected), len(result))
|
||||||
|
for x in expected:
|
||||||
|
found = False
|
||||||
|
for y in result:
|
||||||
|
if x['deviceName'] == y['deviceName']:
|
||||||
|
self.assertSubDictMatch(x, y)
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
self.assertTrue(found)
|
||||||
|
|
||||||
|
def test_describe_instances_bdm(self):
|
||||||
|
"""Make sure describe_instances works with root_device_name and
|
||||||
|
block device mappings
|
||||||
|
"""
|
||||||
|
(inst1, inst2, volumes) = self._setUpBlockDeviceMapping()
|
||||||
|
|
||||||
|
result = self._assertInstance(inst1['id'])
|
||||||
|
self.assertSubDictMatch(self._expected_instance_bdm1, result)
|
||||||
|
self._assertEqualBlockDeviceMapping(
|
||||||
|
self._expected_block_device_mapping0, result['blockDeviceMapping'])
|
||||||
|
|
||||||
|
result = self._assertInstance(inst2['id'])
|
||||||
|
self.assertSubDictMatch(self._expected_instance_bdm2, result)
|
||||||
|
|
||||||
|
self._tearDownBlockDeviceMapping(inst1, inst2, volumes)
|
||||||
|
|
||||||
def test_describe_images(self):
|
def test_describe_images(self):
|
||||||
describe_images = self.cloud.describe_images
|
describe_images = self.cloud.describe_images
|
||||||
|
|
||||||
@@ -445,6 +625,161 @@ class CloudTestCase(test.TestCase):
|
|||||||
self.assertRaises(exception.ImageNotFound, describe_images,
|
self.assertRaises(exception.ImageNotFound, describe_images,
|
||||||
self.context, ['ami-fake'])
|
self.context, ['ami-fake'])
|
||||||
|
|
||||||
|
def assertDictListUnorderedMatch(self, L1, L2, key):
|
||||||
|
self.assertEqual(len(L1), len(L2))
|
||||||
|
for d1 in L1:
|
||||||
|
self.assertTrue(key in d1)
|
||||||
|
for d2 in L2:
|
||||||
|
self.assertTrue(key in d2)
|
||||||
|
if d1[key] == d2[key]:
|
||||||
|
self.assertDictMatch(d1, d2)
|
||||||
|
|
||||||
|
def _setUpImageSet(self, create_volumes_and_snapshots=False):
|
||||||
|
mappings1 = [
|
||||||
|
{'device': '/dev/sda1', 'virtual': 'root'},
|
||||||
|
|
||||||
|
{'device': 'sdb0', 'virtual': 'ephemeral0'},
|
||||||
|
{'device': 'sdb1', 'virtual': 'ephemeral1'},
|
||||||
|
{'device': 'sdb2', 'virtual': 'ephemeral2'},
|
||||||
|
{'device': 'sdb3', 'virtual': 'ephemeral3'},
|
||||||
|
{'device': 'sdb4', 'virtual': 'ephemeral4'},
|
||||||
|
|
||||||
|
{'device': 'sdc0', 'virtual': 'swap'},
|
||||||
|
{'device': 'sdc1', 'virtual': 'swap'},
|
||||||
|
{'device': 'sdc2', 'virtual': 'swap'},
|
||||||
|
{'device': 'sdc3', 'virtual': 'swap'},
|
||||||
|
{'device': 'sdc4', 'virtual': 'swap'}]
|
||||||
|
block_device_mapping1 = [
|
||||||
|
{'device_name': '/dev/sdb1', 'snapshot_id': 01234567},
|
||||||
|
{'device_name': '/dev/sdb2', 'volume_id': 01234567},
|
||||||
|
{'device_name': '/dev/sdb3', 'virtual_name': 'ephemeral5'},
|
||||||
|
{'device_name': '/dev/sdb4', 'no_device': True},
|
||||||
|
|
||||||
|
{'device_name': '/dev/sdc1', 'snapshot_id': 12345678},
|
||||||
|
{'device_name': '/dev/sdc2', 'volume_id': 12345678},
|
||||||
|
{'device_name': '/dev/sdc3', 'virtual_name': 'ephemeral6'},
|
||||||
|
{'device_name': '/dev/sdc4', 'no_device': True}]
|
||||||
|
image1 = {
|
||||||
|
'id': 1,
|
||||||
|
'properties': {
|
||||||
|
'kernel_id': 1,
|
||||||
|
'type': 'machine',
|
||||||
|
'image_state': 'available',
|
||||||
|
'mappings': mappings1,
|
||||||
|
'block_device_mapping': block_device_mapping1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings2 = [{'device': '/dev/sda1', 'virtual': 'root'}]
|
||||||
|
block_device_mapping2 = [{'device_name': '/dev/sdb1',
|
||||||
|
'snapshot_id': 01234567}]
|
||||||
|
image2 = {
|
||||||
|
'id': 2,
|
||||||
|
'properties': {
|
||||||
|
'kernel_id': 2,
|
||||||
|
'type': 'machine',
|
||||||
|
'root_device_name': '/dev/sdb1',
|
||||||
|
'mappings': mappings2,
|
||||||
|
'block_device_mapping': block_device_mapping2}}
|
||||||
|
|
||||||
|
def fake_show(meh, context, image_id):
|
||||||
|
for i in [image1, image2]:
|
||||||
|
if i['id'] == image_id:
|
||||||
|
return i
|
||||||
|
raise exception.ImageNotFound(image_id=image_id)
|
||||||
|
|
||||||
|
def fake_detail(meh, context):
|
||||||
|
return [image1, image2]
|
||||||
|
|
||||||
|
self.stubs.Set(fake._FakeImageService, 'show', fake_show)
|
||||||
|
self.stubs.Set(fake._FakeImageService, 'detail', fake_detail)
|
||||||
|
|
||||||
|
volumes = []
|
||||||
|
snapshots = []
|
||||||
|
if create_volumes_and_snapshots:
|
||||||
|
for bdm in block_device_mapping1:
|
||||||
|
if 'volume_id' in bdm:
|
||||||
|
vol = self._volume_create(bdm['volume_id'])
|
||||||
|
volumes.append(vol['id'])
|
||||||
|
if 'snapshot_id' in bdm:
|
||||||
|
snap = db.snapshot_create(self.context,
|
||||||
|
{'id': bdm['snapshot_id'],
|
||||||
|
'volume_id': 76543210,
|
||||||
|
'status': "available",
|
||||||
|
'volume_size': 1})
|
||||||
|
snapshots.append(snap['id'])
|
||||||
|
return (volumes, snapshots)
|
||||||
|
|
||||||
|
def _assertImageSet(self, result, root_device_type, root_device_name):
|
||||||
|
self.assertEqual(1, len(result['imagesSet']))
|
||||||
|
result = result['imagesSet'][0]
|
||||||
|
self.assertTrue('rootDeviceType' in result)
|
||||||
|
self.assertEqual(result['rootDeviceType'], root_device_type)
|
||||||
|
self.assertTrue('rootDeviceName' in result)
|
||||||
|
self.assertEqual(result['rootDeviceName'], root_device_name)
|
||||||
|
self.assertTrue('blockDeviceMapping' in result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
_expected_root_device_name1 = '/dev/sda1'
|
||||||
|
# NOTE(yamahata): noDevice doesn't make sense when returning mapping
|
||||||
|
# It makes sense only when user overriding existing
|
||||||
|
# mapping.
|
||||||
|
_expected_bdms1 = [
|
||||||
|
{'deviceName': '/dev/sdb0', 'virtualName': 'ephemeral0'},
|
||||||
|
{'deviceName': '/dev/sdb1', 'ebs': {'snapshotId':
|
||||||
|
'snap-00053977'}},
|
||||||
|
{'deviceName': '/dev/sdb2', 'ebs': {'snapshotId':
|
||||||
|
'vol-00053977'}},
|
||||||
|
{'deviceName': '/dev/sdb3', 'virtualName': 'ephemeral5'},
|
||||||
|
# {'deviceName': '/dev/sdb4', 'noDevice': True},
|
||||||
|
|
||||||
|
{'deviceName': '/dev/sdc0', 'virtualName': 'swap'},
|
||||||
|
{'deviceName': '/dev/sdc1', 'ebs': {'snapshotId':
|
||||||
|
'snap-00bc614e'}},
|
||||||
|
{'deviceName': '/dev/sdc2', 'ebs': {'snapshotId':
|
||||||
|
'vol-00bc614e'}},
|
||||||
|
{'deviceName': '/dev/sdc3', 'virtualName': 'ephemeral6'},
|
||||||
|
# {'deviceName': '/dev/sdc4', 'noDevice': True}
|
||||||
|
]
|
||||||
|
|
||||||
|
_expected_root_device_name2 = '/dev/sdb1'
|
||||||
|
_expected_bdms2 = [{'deviceName': '/dev/sdb1',
|
||||||
|
'ebs': {'snapshotId': 'snap-00053977'}}]
|
||||||
|
|
||||||
|
# NOTE(yamahata):
|
||||||
|
# InstanceBlockDeviceMappingItemType
|
||||||
|
# rootDeviceType
|
||||||
|
# rootDeviceName
|
||||||
|
# blockDeviceMapping
|
||||||
|
# deviceName
|
||||||
|
# virtualName
|
||||||
|
# ebs
|
||||||
|
# snapshotId
|
||||||
|
# volumeSize
|
||||||
|
# deleteOnTermination
|
||||||
|
# noDevice
|
||||||
|
def test_describe_image_mapping(self):
|
||||||
|
"""test for rootDeviceName and blockDeiceMapping"""
|
||||||
|
describe_images = self.cloud.describe_images
|
||||||
|
self._setUpImageSet()
|
||||||
|
|
||||||
|
result = describe_images(self.context, ['ami-00000001'])
|
||||||
|
result = self._assertImageSet(result, 'instance-store',
|
||||||
|
self._expected_root_device_name1)
|
||||||
|
|
||||||
|
self.assertDictListUnorderedMatch(result['blockDeviceMapping'],
|
||||||
|
self._expected_bdms1, 'deviceName')
|
||||||
|
|
||||||
|
result = describe_images(self.context, ['ami-00000002'])
|
||||||
|
result = self._assertImageSet(result, 'ebs',
|
||||||
|
self._expected_root_device_name2)
|
||||||
|
|
||||||
|
self.assertDictListUnorderedMatch(result['blockDeviceMapping'],
|
||||||
|
self._expected_bdms2, 'deviceName')
|
||||||
|
|
||||||
|
self.stubs.UnsetAll()
|
||||||
|
|
||||||
def test_describe_image_attribute(self):
|
def test_describe_image_attribute(self):
|
||||||
describe_image_attribute = self.cloud.describe_image_attribute
|
describe_image_attribute = self.cloud.describe_image_attribute
|
||||||
|
|
||||||
@@ -459,6 +794,32 @@ class CloudTestCase(test.TestCase):
|
|||||||
'launchPermission')
|
'launchPermission')
|
||||||
self.assertEqual([{'group': 'all'}], result['launchPermission'])
|
self.assertEqual([{'group': 'all'}], result['launchPermission'])
|
||||||
|
|
||||||
|
def test_describe_image_attribute_root_device_name(self):
|
||||||
|
describe_image_attribute = self.cloud.describe_image_attribute
|
||||||
|
self._setUpImageSet()
|
||||||
|
|
||||||
|
result = describe_image_attribute(self.context, 'ami-00000001',
|
||||||
|
'rootDeviceName')
|
||||||
|
self.assertEqual(result['rootDeviceName'],
|
||||||
|
self._expected_root_device_name1)
|
||||||
|
result = describe_image_attribute(self.context, 'ami-00000002',
|
||||||
|
'rootDeviceName')
|
||||||
|
self.assertEqual(result['rootDeviceName'],
|
||||||
|
self._expected_root_device_name2)
|
||||||
|
|
||||||
|
def test_describe_image_attribute_block_device_mapping(self):
|
||||||
|
describe_image_attribute = self.cloud.describe_image_attribute
|
||||||
|
self._setUpImageSet()
|
||||||
|
|
||||||
|
result = describe_image_attribute(self.context, 'ami-00000001',
|
||||||
|
'blockDeviceMapping')
|
||||||
|
self.assertDictListUnorderedMatch(result['blockDeviceMapping'],
|
||||||
|
self._expected_bdms1, 'deviceName')
|
||||||
|
result = describe_image_attribute(self.context, 'ami-00000002',
|
||||||
|
'blockDeviceMapping')
|
||||||
|
self.assertDictListUnorderedMatch(result['blockDeviceMapping'],
|
||||||
|
self._expected_bdms2, 'deviceName')
|
||||||
|
|
||||||
def test_modify_image_attribute(self):
|
def test_modify_image_attribute(self):
|
||||||
modify_image_attribute = self.cloud.modify_image_attribute
|
modify_image_attribute = self.cloud.modify_image_attribute
|
||||||
|
|
||||||
@@ -699,7 +1060,7 @@ class CloudTestCase(test.TestCase):
|
|||||||
def test_update_of_volume_display_fields(self):
|
def test_update_of_volume_display_fields(self):
|
||||||
vol = db.volume_create(self.context, {})
|
vol = db.volume_create(self.context, {})
|
||||||
self.cloud.update_volume(self.context,
|
self.cloud.update_volume(self.context,
|
||||||
ec2utils.id_to_ec2_id(vol['id'], 'vol-%08x'),
|
ec2utils.id_to_ec2_vol_id(vol['id']),
|
||||||
display_name='c00l v0lum3')
|
display_name='c00l v0lum3')
|
||||||
vol = db.volume_get(self.context, vol['id'])
|
vol = db.volume_get(self.context, vol['id'])
|
||||||
self.assertEqual('c00l v0lum3', vol['display_name'])
|
self.assertEqual('c00l v0lum3', vol['display_name'])
|
||||||
@@ -708,7 +1069,7 @@ class CloudTestCase(test.TestCase):
|
|||||||
def test_update_of_volume_wont_update_private_fields(self):
|
def test_update_of_volume_wont_update_private_fields(self):
|
||||||
vol = db.volume_create(self.context, {})
|
vol = db.volume_create(self.context, {})
|
||||||
self.cloud.update_volume(self.context,
|
self.cloud.update_volume(self.context,
|
||||||
ec2utils.id_to_ec2_id(vol['id'], 'vol-%08x'),
|
ec2utils.id_to_ec2_vol_id(vol['id']),
|
||||||
mountpoint='/not/here')
|
mountpoint='/not/here')
|
||||||
vol = db.volume_get(self.context, vol['id'])
|
vol = db.volume_get(self.context, vol['id'])
|
||||||
self.assertEqual(None, vol['mountpoint'])
|
self.assertEqual(None, vol['mountpoint'])
|
||||||
@@ -786,11 +1147,13 @@ class CloudTestCase(test.TestCase):
|
|||||||
|
|
||||||
self._restart_compute_service()
|
self._restart_compute_service()
|
||||||
|
|
||||||
def _volume_create(self):
|
def _volume_create(self, volume_id=None):
|
||||||
kwargs = {'status': 'available',
|
kwargs = {'status': 'available',
|
||||||
'host': self.volume.host,
|
'host': self.volume.host,
|
||||||
'size': 1,
|
'size': 1,
|
||||||
'attach_status': 'detached', }
|
'attach_status': 'detached', }
|
||||||
|
if volume_id:
|
||||||
|
kwargs['id'] = volume_id
|
||||||
return db.volume_create(self.context, kwargs)
|
return db.volume_create(self.context, kwargs)
|
||||||
|
|
||||||
def _assert_volume_attached(self, vol, instance_id, mountpoint):
|
def _assert_volume_attached(self, vol, instance_id, mountpoint):
|
||||||
@@ -819,10 +1182,10 @@ class CloudTestCase(test.TestCase):
|
|||||||
'max_count': 1,
|
'max_count': 1,
|
||||||
'block_device_mapping': [{'device_name': '/dev/vdb',
|
'block_device_mapping': [{'device_name': '/dev/vdb',
|
||||||
'volume_id': vol1['id'],
|
'volume_id': vol1['id'],
|
||||||
'delete_on_termination': False, },
|
'delete_on_termination': False},
|
||||||
{'device_name': '/dev/vdc',
|
{'device_name': '/dev/vdc',
|
||||||
'volume_id': vol2['id'],
|
'volume_id': vol2['id'],
|
||||||
'delete_on_termination': True, },
|
'delete_on_termination': True},
|
||||||
]}
|
]}
|
||||||
ec2_instance_id = self._run_instance_wait(**kwargs)
|
ec2_instance_id = self._run_instance_wait(**kwargs)
|
||||||
instance_id = ec2utils.ec2_id_to_id(ec2_instance_id)
|
instance_id = ec2utils.ec2_id_to_id(ec2_instance_id)
|
||||||
@@ -954,7 +1317,7 @@ class CloudTestCase(test.TestCase):
|
|||||||
def test_run_with_snapshot(self):
|
def test_run_with_snapshot(self):
|
||||||
"""Makes sure run/stop/start instance with snapshot works."""
|
"""Makes sure run/stop/start instance with snapshot works."""
|
||||||
vol = self._volume_create()
|
vol = self._volume_create()
|
||||||
ec2_volume_id = ec2utils.id_to_ec2_id(vol['id'], 'vol-%08x')
|
ec2_volume_id = ec2utils.id_to_ec2_vol_id(vol['id'])
|
||||||
|
|
||||||
ec2_snapshot1_id = self._create_snapshot(ec2_volume_id)
|
ec2_snapshot1_id = self._create_snapshot(ec2_volume_id)
|
||||||
snapshot1_id = ec2utils.ec2_id_to_id(ec2_snapshot1_id)
|
snapshot1_id = ec2utils.ec2_id_to_id(ec2_snapshot1_id)
|
||||||
@@ -1013,3 +1376,33 @@ class CloudTestCase(test.TestCase):
|
|||||||
self.cloud.delete_snapshot(self.context, snapshot_id)
|
self.cloud.delete_snapshot(self.context, snapshot_id)
|
||||||
greenthread.sleep(0.3)
|
greenthread.sleep(0.3)
|
||||||
db.volume_destroy(self.context, vol['id'])
|
db.volume_destroy(self.context, vol['id'])
|
||||||
|
|
||||||
|
def test_create_image(self):
|
||||||
|
"""Make sure that CreateImage works"""
|
||||||
|
# enforce periodic tasks run in short time to avoid wait for 60s.
|
||||||
|
self._restart_compute_service(periodic_interval=0.3)
|
||||||
|
|
||||||
|
(volumes, snapshots) = self._setUpImageSet(
|
||||||
|
create_volumes_and_snapshots=True)
|
||||||
|
|
||||||
|
kwargs = {'image_id': 'ami-1',
|
||||||
|
'instance_type': FLAGS.default_instance_type,
|
||||||
|
'max_count': 1}
|
||||||
|
ec2_instance_id = self._run_instance_wait(**kwargs)
|
||||||
|
|
||||||
|
# TODO(yamahata): s3._s3_create() can't be tested easily by unit test
|
||||||
|
# as there is no unit test for s3.create()
|
||||||
|
## result = self.cloud.create_image(self.context, ec2_instance_id,
|
||||||
|
## no_reboot=True)
|
||||||
|
## ec2_image_id = result['imageId']
|
||||||
|
## created_image = self.cloud.describe_images(self.context,
|
||||||
|
## [ec2_image_id])
|
||||||
|
|
||||||
|
self.cloud.terminate_instances(self.context, [ec2_instance_id])
|
||||||
|
for vol in volumes:
|
||||||
|
db.volume_destroy(self.context, vol)
|
||||||
|
for snap in snapshots:
|
||||||
|
db.snapshot_destroy(self.context, snap)
|
||||||
|
# TODO(yamahata): clean up snapshot created by CreateImage.
|
||||||
|
|
||||||
|
self._restart_compute_service()
|
||||||
|
|||||||
@@ -818,3 +818,114 @@ class ComputeTestCase(test.TestCase):
|
|||||||
LOG.info(_("After force-killing instances: %s"), instances)
|
LOG.info(_("After force-killing instances: %s"), instances)
|
||||||
self.assertEqual(len(instances), 1)
|
self.assertEqual(len(instances), 1)
|
||||||
self.assertEqual(power_state.SHUTOFF, instances[0]['state'])
|
self.assertEqual(power_state.SHUTOFF, instances[0]['state'])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _parse_db_block_device_mapping(bdm_ref):
|
||||||
|
attr_list = ('delete_on_termination', 'device_name', 'no_device',
|
||||||
|
'virtual_name', 'volume_id', 'volume_size', 'snapshot_id')
|
||||||
|
bdm = {}
|
||||||
|
for attr in attr_list:
|
||||||
|
val = bdm_ref.get(attr, None)
|
||||||
|
if val:
|
||||||
|
bdm[attr] = val
|
||||||
|
|
||||||
|
return bdm
|
||||||
|
|
||||||
|
def test_update_block_device_mapping(self):
|
||||||
|
instance_id = self._create_instance()
|
||||||
|
mappings = [
|
||||||
|
{'virtual': 'ami', 'device': 'sda1'},
|
||||||
|
{'virtual': 'root', 'device': '/dev/sda1'},
|
||||||
|
|
||||||
|
{'virtual': 'swap', 'device': 'sdb1'},
|
||||||
|
{'virtual': 'swap', 'device': 'sdb2'},
|
||||||
|
{'virtual': 'swap', 'device': 'sdb3'},
|
||||||
|
{'virtual': 'swap', 'device': 'sdb4'},
|
||||||
|
|
||||||
|
{'virtual': 'ephemeral0', 'device': 'sdc1'},
|
||||||
|
{'virtual': 'ephemeral1', 'device': 'sdc2'},
|
||||||
|
{'virtual': 'ephemeral2', 'device': 'sdc3'}]
|
||||||
|
block_device_mapping = [
|
||||||
|
# root
|
||||||
|
{'device_name': '/dev/sda1',
|
||||||
|
'snapshot_id': 0x12345678,
|
||||||
|
'delete_on_termination': False},
|
||||||
|
|
||||||
|
|
||||||
|
# overwrite swap
|
||||||
|
{'device_name': '/dev/sdb2',
|
||||||
|
'snapshot_id': 0x23456789,
|
||||||
|
'delete_on_termination': False},
|
||||||
|
{'device_name': '/dev/sdb3',
|
||||||
|
'snapshot_id': 0x3456789A},
|
||||||
|
{'device_name': '/dev/sdb4',
|
||||||
|
'no_device': True},
|
||||||
|
|
||||||
|
# overwrite ephemeral
|
||||||
|
{'device_name': '/dev/sdc2',
|
||||||
|
'snapshot_id': 0x456789AB,
|
||||||
|
'delete_on_termination': False},
|
||||||
|
{'device_name': '/dev/sdc3',
|
||||||
|
'snapshot_id': 0x56789ABC},
|
||||||
|
{'device_name': '/dev/sdc4',
|
||||||
|
'no_device': True},
|
||||||
|
|
||||||
|
# volume
|
||||||
|
{'device_name': '/dev/sdd1',
|
||||||
|
'snapshot_id': 0x87654321,
|
||||||
|
'delete_on_termination': False},
|
||||||
|
{'device_name': '/dev/sdd2',
|
||||||
|
'snapshot_id': 0x98765432},
|
||||||
|
{'device_name': '/dev/sdd3',
|
||||||
|
'snapshot_id': 0xA9875463},
|
||||||
|
{'device_name': '/dev/sdd4',
|
||||||
|
'no_device': True}]
|
||||||
|
|
||||||
|
self.compute_api._update_image_block_device_mapping(
|
||||||
|
self.context, instance_id, mappings)
|
||||||
|
|
||||||
|
bdms = [self._parse_db_block_device_mapping(bdm_ref)
|
||||||
|
for bdm_ref in db.block_device_mapping_get_all_by_instance(
|
||||||
|
self.context, instance_id)]
|
||||||
|
expected_result = [
|
||||||
|
{'virtual_name': 'swap', 'device_name': '/dev/sdb1'},
|
||||||
|
{'virtual_name': 'swap', 'device_name': '/dev/sdb2'},
|
||||||
|
{'virtual_name': 'swap', 'device_name': '/dev/sdb3'},
|
||||||
|
{'virtual_name': 'swap', 'device_name': '/dev/sdb4'},
|
||||||
|
{'virtual_name': 'ephemeral0', 'device_name': '/dev/sdc1'},
|
||||||
|
{'virtual_name': 'ephemeral1', 'device_name': '/dev/sdc2'},
|
||||||
|
{'virtual_name': 'ephemeral2', 'device_name': '/dev/sdc3'}]
|
||||||
|
bdms.sort()
|
||||||
|
expected_result.sort()
|
||||||
|
self.assertDictListMatch(bdms, expected_result)
|
||||||
|
|
||||||
|
self.compute_api._update_block_device_mapping(
|
||||||
|
self.context, instance_id, block_device_mapping)
|
||||||
|
bdms = [self._parse_db_block_device_mapping(bdm_ref)
|
||||||
|
for bdm_ref in db.block_device_mapping_get_all_by_instance(
|
||||||
|
self.context, instance_id)]
|
||||||
|
expected_result = [
|
||||||
|
{'snapshot_id': 0x12345678, 'device_name': '/dev/sda1'},
|
||||||
|
|
||||||
|
{'virtual_name': 'swap', 'device_name': '/dev/sdb1'},
|
||||||
|
{'snapshot_id': 0x23456789, 'device_name': '/dev/sdb2'},
|
||||||
|
{'snapshot_id': 0x3456789A, 'device_name': '/dev/sdb3'},
|
||||||
|
{'no_device': True, 'device_name': '/dev/sdb4'},
|
||||||
|
|
||||||
|
{'virtual_name': 'ephemeral0', 'device_name': '/dev/sdc1'},
|
||||||
|
{'snapshot_id': 0x456789AB, 'device_name': '/dev/sdc2'},
|
||||||
|
{'snapshot_id': 0x56789ABC, 'device_name': '/dev/sdc3'},
|
||||||
|
{'no_device': True, 'device_name': '/dev/sdc4'},
|
||||||
|
|
||||||
|
{'snapshot_id': 0x87654321, 'device_name': '/dev/sdd1'},
|
||||||
|
{'snapshot_id': 0x98765432, 'device_name': '/dev/sdd2'},
|
||||||
|
{'snapshot_id': 0xA9875463, 'device_name': '/dev/sdd3'},
|
||||||
|
{'no_device': True, 'device_name': '/dev/sdd4'}]
|
||||||
|
bdms.sort()
|
||||||
|
expected_result.sort()
|
||||||
|
self.assertDictListMatch(bdms, expected_result)
|
||||||
|
|
||||||
|
for bdm in db.block_device_mapping_get_all_by_instance(
|
||||||
|
self.context, instance_id):
|
||||||
|
db.block_device_mapping_destroy(self.context, bdm['id'])
|
||||||
|
self.compute.terminate_instance(self.context, instance_id)
|
||||||
|
|||||||
@@ -27,8 +27,10 @@ from nova import exception
|
|||||||
from nova import db
|
from nova import db
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
|
from nova import rpc
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
from nova import volume
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
LOG = logging.getLogger('nova.tests.volume')
|
LOG = logging.getLogger('nova.tests.volume')
|
||||||
@@ -43,6 +45,11 @@ class VolumeTestCase(test.TestCase):
|
|||||||
self.flags(connection_type='fake')
|
self.flags(connection_type='fake')
|
||||||
self.volume = utils.import_object(FLAGS.volume_manager)
|
self.volume = utils.import_object(FLAGS.volume_manager)
|
||||||
self.context = context.get_admin_context()
|
self.context = context.get_admin_context()
|
||||||
|
self.instance_id = db.instance_create(self.context, {})['id']
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
db.instance_destroy(self.context, self.instance_id)
|
||||||
|
super(VolumeTestCase, self).tearDown()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_volume(size='0', snapshot_id=None):
|
def _create_volume(size='0', snapshot_id=None):
|
||||||
@@ -223,6 +230,30 @@ class VolumeTestCase(test.TestCase):
|
|||||||
snapshot_id)
|
snapshot_id)
|
||||||
self.volume.delete_volume(self.context, volume_id)
|
self.volume.delete_volume(self.context, volume_id)
|
||||||
|
|
||||||
|
def test_create_snapshot_force(self):
|
||||||
|
"""Test snapshot in use can be created forcibly."""
|
||||||
|
|
||||||
|
def fake_cast(ctxt, topic, msg):
|
||||||
|
pass
|
||||||
|
self.stubs.Set(rpc, 'cast', fake_cast)
|
||||||
|
|
||||||
|
volume_id = self._create_volume()
|
||||||
|
self.volume.create_volume(self.context, volume_id)
|
||||||
|
db.volume_attached(self.context, volume_id, self.instance_id,
|
||||||
|
'/dev/sda1')
|
||||||
|
|
||||||
|
volume_api = volume.api.API()
|
||||||
|
self.assertRaises(exception.ApiError,
|
||||||
|
volume_api.create_snapshot,
|
||||||
|
self.context, volume_id,
|
||||||
|
'fake_name', 'fake_description')
|
||||||
|
snapshot_ref = volume_api.create_snapshot_force(self.context,
|
||||||
|
volume_id,
|
||||||
|
'fake_name',
|
||||||
|
'fake_description')
|
||||||
|
db.snapshot_destroy(self.context, snapshot_ref['id'])
|
||||||
|
db.volume_destroy(self.context, volume_id)
|
||||||
|
|
||||||
|
|
||||||
class DriverTestCase(test.TestCase):
|
class DriverTestCase(test.TestCase):
|
||||||
"""Base Test class for Drivers."""
|
"""Base Test class for Drivers."""
|
||||||
|
|||||||
Reference in New Issue
Block a user