Merge "Boot from image destination - volume"

This commit is contained in:
Jenkins 2013-08-21 19:12:38 +00:00 committed by Gerrit Code Review
commit 0ee03b0730
5 changed files with 186 additions and 25 deletions

View File

@ -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

View File

@ -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))
}

View File

@ -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))

View File

@ -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'],

View File

@ -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),