Merge "Boot from image destination - volume"
This commit is contained in:
commit
0ee03b0730
@ -203,6 +203,7 @@ class BlockDeviceDict(dict):
|
||||
for field in copy_over_fields if field in self)
|
||||
|
||||
source_type = self.get('source_type')
|
||||
destination_type = self.get('destination_type')
|
||||
no_device = self.get('no_device')
|
||||
if source_type == 'blank':
|
||||
if self['guest_format'] == 'swap':
|
||||
@ -214,9 +215,11 @@ class BlockDeviceDict(dict):
|
||||
elif source_type in ('volume', 'snapshot') or no_device:
|
||||
legacy_block_device['virtual_name'] = None
|
||||
elif source_type == 'image':
|
||||
# NOTE(ndipanov): Image bdms have no meaning in
|
||||
# the legacy format - raise
|
||||
raise exception.InvalidBDMForLegacy()
|
||||
if destination_type != 'volume':
|
||||
# NOTE(ndipanov): Image bdms with local destination
|
||||
# have no meaning in the legacy format - raise
|
||||
raise exception.InvalidBDMForLegacy()
|
||||
legacy_block_device['virtual_name'] = None
|
||||
|
||||
return legacy_block_device
|
||||
|
||||
|
@ -895,7 +895,7 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
while attempts < max_tries:
|
||||
volume = self.volume_api.get(context, vol_id)
|
||||
volume_status = volume['status']
|
||||
if volume_status != 'creating':
|
||||
if volume_status not in ['creating', 'downloading']:
|
||||
if volume_status != 'available':
|
||||
LOG.warn(_("Volume id: %s finished being created but was"
|
||||
" not set as 'available'"), vol_id)
|
||||
@ -1284,6 +1284,11 @@ class ComputeManager(manager.SchedulerDependentManager):
|
||||
driver_block_device.convert_snapshots(bdms),
|
||||
context, instance, self.volume_api,
|
||||
self.driver, self.conductor_api,
|
||||
self._await_block_device_map_created) +
|
||||
driver_block_device.attach_block_devices(
|
||||
driver_block_device.convert_images(bdms),
|
||||
context, instance, self.volume_api,
|
||||
self.driver, self.conductor_api,
|
||||
self._await_block_device_map_created))
|
||||
}
|
||||
|
||||
|
@ -224,6 +224,30 @@ class TestBlockDeviceDict(test.TestCase):
|
||||
'device_name': '/dev/vdc'},
|
||||
]
|
||||
|
||||
self.new_mapping_source_image = [
|
||||
BDM({'id': 6, 'instance_uuid': 'fake-instance',
|
||||
'device_name': '/dev/sda3',
|
||||
'source_type': 'image',
|
||||
'destination_type': 'volume',
|
||||
'connection_info': "{'fake': 'connection_info'}",
|
||||
'volume_id': 'fake-volume-id-3',
|
||||
'boot_index': -1}),
|
||||
BDM({'id': 7, 'instance_uuid': 'fake-instance',
|
||||
'device_name': '/dev/sda4',
|
||||
'source_type': 'image',
|
||||
'destination_type': 'local',
|
||||
'connection_info': "{'fake': 'connection_info'}",
|
||||
'image_id': 'fake-image-id-2',
|
||||
'boot_index': -1}),
|
||||
]
|
||||
|
||||
self.legacy_mapping_source_image = [
|
||||
{'id': 6, 'instance_uuid': 'fake-instance',
|
||||
'device_name': '/dev/sda3',
|
||||
'connection_info': "{'fake': 'connection_info'}",
|
||||
'volume_id': 'fake-volume-id-3'},
|
||||
]
|
||||
|
||||
def test_init(self):
|
||||
def fake_validate(obj, dct):
|
||||
pass
|
||||
@ -366,3 +390,17 @@ class TestBlockDeviceDict(test.TestCase):
|
||||
|
||||
for legacy, expected in zip(got_legacy, self.legacy_mapping):
|
||||
self.assertThat(expected, matchers.IsSubDictOf(legacy))
|
||||
|
||||
def test_legacy_source_image(self):
|
||||
for legacy, new in zip(self.legacy_mapping_source_image,
|
||||
self.new_mapping_source_image):
|
||||
if new['destination_type'] == 'volume':
|
||||
self.assertThat(legacy, matchers.IsSubDictOf(new.legacy()))
|
||||
else:
|
||||
self.assertRaises(exception.InvalidBDMForLegacy, new.legacy)
|
||||
|
||||
def test_legacy_mapping_source_image(self):
|
||||
got_legacy = block_device.legacy_mapping(self.new_mapping)
|
||||
|
||||
for legacy, expected in zip(got_legacy, self.legacy_mapping):
|
||||
self.assertThat(expected, matchers.IsSubDictOf(legacy))
|
||||
|
@ -30,7 +30,8 @@ class TestDriverBlockDevice(test.TestCase):
|
||||
'swap': driver_block_device.DriverSwapBlockDevice,
|
||||
'ephemeral': driver_block_device.DriverEphemeralBlockDevice,
|
||||
'volume': driver_block_device.DriverVolumeBlockDevice,
|
||||
'snapshot': driver_block_device.DriverSnapshotBlockDevice
|
||||
'snapshot': driver_block_device.DriverSnapshotBlockDevice,
|
||||
'image': driver_block_device.DriverImageBlockDevice
|
||||
}
|
||||
|
||||
swap_bdm = block_device.BlockDeviceDict(
|
||||
@ -134,6 +135,34 @@ class TestDriverBlockDevice(test.TestCase):
|
||||
'connection_info': {"fake": "connection_info"},
|
||||
'delete_on_termination': True}
|
||||
|
||||
image_bdm = block_device.BlockDeviceDict(
|
||||
{'id': 5, 'instance_uuid': 'fake-instance',
|
||||
'device_name': '/dev/sda2',
|
||||
'delete_on_termination': True,
|
||||
'volume_size': 1,
|
||||
'disk_bus': 'scsi',
|
||||
'device_type': 'disk',
|
||||
'source_type': 'image',
|
||||
'destination_type': 'volume',
|
||||
'connection_info': '{"fake": "connection_info"}',
|
||||
'image_id': 'fake-image-id-1',
|
||||
'volume_id': 'fake-volume-id-2',
|
||||
'boot_index': -1})
|
||||
|
||||
image_driver_bdm = {
|
||||
'mount_device': '/dev/sda2',
|
||||
'connection_info': {"fake": "connection_info"},
|
||||
'delete_on_termination': True,
|
||||
'disk_bus': 'scsi',
|
||||
'device_type': 'disk',
|
||||
'guest_format': None,
|
||||
'boot_index': -1}
|
||||
|
||||
image_legacy_driver_bdm = {
|
||||
'mount_device': '/dev/sda2',
|
||||
'connection_info': {"fake": "connection_info"},
|
||||
'delete_on_termination': True}
|
||||
|
||||
def setUp(self):
|
||||
super(TestDriverBlockDevice, self).setUp()
|
||||
self.volume_api = self.mox.CreateMock(cinder.API)
|
||||
@ -204,6 +233,22 @@ class TestDriverBlockDevice(test.TestCase):
|
||||
self.assertEquals(test_bdm.volume_id, 'fake-volume-id-2')
|
||||
self.assertEquals(test_bdm.volume_size, 3)
|
||||
|
||||
def test_driver_image_block_device(self):
|
||||
self._test_driver_device('image')
|
||||
|
||||
test_bdm = self.driver_classes['image'](
|
||||
self.image_bdm)
|
||||
self.assertEquals(test_bdm.id, 5)
|
||||
self.assertEquals(test_bdm.image_id, 'fake-image-id-1')
|
||||
self.assertEquals(test_bdm.volume_size, 1)
|
||||
|
||||
def test_driver_image_block_device_destination_local(self):
|
||||
self._test_driver_device('image')
|
||||
bdm = self.image_bdm.copy()
|
||||
bdm['destination_type'] = 'local'
|
||||
self.assertRaises(driver_block_device._InvalidType,
|
||||
self.driver_classes['image'], bdm)
|
||||
|
||||
def test_volume_attach(self):
|
||||
test_bdm = self.driver_classes['volume'](
|
||||
self.volume_bdm)
|
||||
@ -315,6 +360,56 @@ class TestDriverBlockDevice(test.TestCase):
|
||||
self.virt_driver, self.db_api)
|
||||
self.assertEquals(test_bdm.volume_id, 'fake-volume-id-2')
|
||||
|
||||
def test_image_attach_no_volume(self):
|
||||
no_volume_image = self.image_bdm.copy()
|
||||
no_volume_image['volume_id'] = None
|
||||
test_bdm = self.driver_classes['image'](no_volume_image)
|
||||
|
||||
instance = {'id': 'fake_id', 'uuid': 'fake_uuid'}
|
||||
image = {'id': 'fake-image-id-1'}
|
||||
volume = {'id': 'fake-volume-id-2'}
|
||||
|
||||
wait_func = self.mox.CreateMockAnything()
|
||||
volume_class = self.driver_classes['volume']
|
||||
self.mox.StubOutWithMock(volume_class, 'attach')
|
||||
|
||||
self.volume_api.create(self.context, 1,
|
||||
'', '', image_id=image['id']).AndReturn(volume)
|
||||
wait_func(self.context, 'fake-volume-id-2').AndReturn(None)
|
||||
self.db_api.block_device_mapping_update(
|
||||
self.context, 5, {'volume_id': 'fake-volume-id-2'}).AndReturn(None)
|
||||
volume_class.attach(self.context, instance, self.volume_api,
|
||||
self.virt_driver, self.db_api).AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
test_bdm.attach(self.context, instance, self.volume_api,
|
||||
self.virt_driver, self.db_api, wait_func)
|
||||
self.assertEquals(test_bdm.volume_id, 'fake-volume-id-2')
|
||||
|
||||
def test_image_attach_volume(self):
|
||||
test_bdm = self.driver_classes['image'](
|
||||
self.image_bdm)
|
||||
|
||||
instance = {'id': 'fake_id', 'uuid': 'fake_uuid'}
|
||||
|
||||
volume_class = self.driver_classes['volume']
|
||||
self.mox.StubOutWithMock(volume_class, 'attach')
|
||||
|
||||
# Make sure theses are not called
|
||||
self.mox.StubOutWithMock(self.volume_api, 'get_snapshot')
|
||||
self.mox.StubOutWithMock(self.volume_api, 'create')
|
||||
self.mox.StubOutWithMock(self.db_api,
|
||||
'block_device_mapping_update')
|
||||
|
||||
volume_class.attach(self.context, instance, self.volume_api,
|
||||
self.virt_driver, self.db_api).AndReturn(None)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
test_bdm.attach(self.context, instance, self.volume_api,
|
||||
self.virt_driver, self.db_api)
|
||||
self.assertEquals(test_bdm.volume_id, 'fake-volume-id-2')
|
||||
|
||||
def test_convert_block_devices(self):
|
||||
converted = driver_block_device._convert_block_devices(
|
||||
self.driver_classes['volume'],
|
||||
|
@ -116,8 +116,17 @@ class DriverVolumeBlockDevice(DriverBlockDevice):
|
||||
'disk_bus', 'boot_index'])
|
||||
_fields = _legacy_fields | _new_fields
|
||||
|
||||
_valid_source = 'volume'
|
||||
_valid_destination = 'volume'
|
||||
|
||||
# Override in subclasses if volume should be created from
|
||||
# another source.
|
||||
_source_id_field = None
|
||||
|
||||
def _transform(self, bdm):
|
||||
if not bdm.get('source_type') == 'volume':
|
||||
if not bdm.get('source_type') == self._valid_source\
|
||||
or not bdm.get('destination_type') == \
|
||||
self._valid_destination:
|
||||
raise _InvalidType
|
||||
|
||||
# NOTE (ndipanov): Save it as an attribute as we will
|
||||
@ -125,6 +134,9 @@ class DriverVolumeBlockDevice(DriverBlockDevice):
|
||||
self.volume_size = bdm.get('volume_size')
|
||||
self.volume_id = bdm.get('volume_id')
|
||||
|
||||
if self._source_id_field:
|
||||
setattr(self, self._source_id_field,
|
||||
bdm.get(self._source_id_field, None))
|
||||
self.update(
|
||||
dict((k, v) for k, v in bdm.iteritems()
|
||||
if k in self._new_fields | set(['delete_on_termination']))
|
||||
@ -187,26 +199,9 @@ class DriverVolumeBlockDevice(DriverBlockDevice):
|
||||
|
||||
|
||||
class DriverSnapshotBlockDevice(DriverVolumeBlockDevice):
|
||||
def _transform(self, bdm):
|
||||
if not bdm.get('source_type') == 'snapshot':
|
||||
raise _InvalidType
|
||||
|
||||
# NOTE (ndipanov): Save these as attributes as we will
|
||||
# need them for attach()
|
||||
self.volume_size = bdm.get('volume_size')
|
||||
self.snapshot_id = bdm.get('snapshot_id')
|
||||
self.volume_id = bdm.get('volume_id')
|
||||
|
||||
self.update(
|
||||
dict((k, v) for k, v in bdm.iteritems()
|
||||
if k in self._new_fields | set(['delete_on_termination']))
|
||||
)
|
||||
self['mount_device'] = bdm.get('device_name')
|
||||
try:
|
||||
self['connection_info'] = jsonutils.loads(
|
||||
bdm.get('connection_info'))
|
||||
except TypeError:
|
||||
self['connection_info'] = None
|
||||
_valid_source = 'snapshot'
|
||||
_source_id_field = 'snapshot_id'
|
||||
|
||||
def attach(self, context, instance, volume_api, virt_driver,
|
||||
db_api=None, wait_func=None):
|
||||
@ -229,6 +224,28 @@ class DriverSnapshotBlockDevice(DriverVolumeBlockDevice):
|
||||
db_api)
|
||||
|
||||
|
||||
class DriverImageBlockDevice(DriverVolumeBlockDevice):
|
||||
|
||||
_valid_source = 'image'
|
||||
_source_id_field = 'image_id'
|
||||
|
||||
def attach(self, context, instance, volume_api, virt_driver,
|
||||
db_api=None, wait_func=None):
|
||||
if not self.volume_id:
|
||||
vol = volume_api.create(context, self.volume_size,
|
||||
'', '', image_id=self.image_id)
|
||||
if wait_func:
|
||||
wait_func(context, vol['id'])
|
||||
if db_api:
|
||||
db_api.block_device_mapping_update(context, self.id,
|
||||
{'volume_id': vol['id']})
|
||||
self.volume_id = vol['id']
|
||||
|
||||
super(DriverImageBlockDevice, self).attach(context, instance,
|
||||
volume_api, virt_driver,
|
||||
db_api)
|
||||
|
||||
|
||||
def _convert_block_devices(device_type, block_device_mapping):
|
||||
def _is_transformable(bdm):
|
||||
try:
|
||||
@ -257,6 +274,9 @@ convert_volumes = functools.partial(_convert_block_devices,
|
||||
convert_snapshots = functools.partial(_convert_block_devices,
|
||||
DriverSnapshotBlockDevice)
|
||||
|
||||
convert_images = functools.partial(_convert_block_devices,
|
||||
DriverImageBlockDevice)
|
||||
|
||||
|
||||
def attach_block_devices(block_device_mapping, *attach_args, **attach_kwargs):
|
||||
map(operator.methodcaller('attach', *attach_args, **attach_kwargs),
|
||||
|
Loading…
x
Reference in New Issue
Block a user