Merge "Allow empty volumes to be created"

This commit is contained in:
Jenkins 2014-08-12 12:53:06 +00:00 committed by Gerrit Code Review
commit d003a85520
9 changed files with 300 additions and 16 deletions

View File

@ -186,6 +186,9 @@ class BlockDeviceDict(dict):
if source_type not in ('volume', 'image', 'snapshot', 'blank'):
raise exception.InvalidBDMFormat(
details=_("Invalid source_type field."))
elif source_type == 'blank' and device_uuid:
raise exception.InvalidBDMFormat(
details=_("Invalid device UUID."))
elif source_type != 'blank':
if not device_uuid:
raise exception.InvalidBDMFormat(
@ -411,8 +414,9 @@ def new_format_is_swap(bdm):
def new_format_is_ephemeral(bdm):
if (bdm.get('source_type') == 'blank' and not
new_format_is_swap(bdm)):
if (bdm.get('source_type') == 'blank' and
bdm.get('destination_type') == 'local' and
bdm.get('guest_format') != 'swap'):
return True
return False

View File

@ -1694,8 +1694,9 @@ class ComputeManager(manager.Manager):
root_bdm.save()
def _is_mapping(bdm):
return (bdm.source_type in ('image', 'volume', 'snapshot') and
driver_block_device.is_implemented(bdm))
return (bdm.source_type in ('image', 'volume', 'snapshot', 'blank')
and bdm.destination_type == 'volume'
and driver_block_device.is_implemented(bdm))
ephemerals = filter(block_device.new_format_is_ephemeral,
block_devices)
@ -1731,6 +1732,11 @@ class ComputeManager(manager.Manager):
driver_block_device.convert_images(bdms),
context, instance, self.volume_api,
self.driver, self._await_block_device_map_created,
do_check_attach=do_check_attach) +
driver_block_device.attach_block_devices(
driver_block_device.convert_blanks(bdms),
context, instance, self.volume_api,
self.driver, self._await_block_device_map_created,
do_check_attach=do_check_attach))
}

View File

@ -1094,6 +1094,80 @@ class ComputeVolumeTestCase(BaseTestCase):
self.context, instance, bdms)
self.assertTrue(mock_create.called)
@mock.patch.object(nova.virt.block_device, 'get_swap')
@mock.patch.object(nova.virt.block_device, 'convert_blanks')
@mock.patch.object(nova.virt.block_device, 'convert_images')
@mock.patch.object(nova.virt.block_device, 'convert_snapshots')
@mock.patch.object(nova.virt.block_device, 'convert_volumes')
@mock.patch.object(nova.virt.block_device, 'convert_ephemerals')
@mock.patch.object(nova.virt.block_device, 'convert_swap')
@mock.patch.object(nova.virt.block_device, 'attach_block_devices')
def test_prep_block_device_with_blanks(self, attach_block_devices,
convert_swap, convert_ephemerals,
convert_volumes, convert_snapshots,
convert_images, convert_blanks,
get_swap):
instance = self._create_fake_instance()
instance['root_device_name'] = '/dev/vda'
root_volume = objects.BlockDeviceMapping(
**fake_block_device.FakeDbBlockDeviceDict({
'instance_uuid': 'fake-instance',
'source_type': 'image',
'destination_type': 'volume',
'image_id': 'fake-image-id-1',
'volume_size': 1,
'boot_index': 0}))
blank_volume1 = objects.BlockDeviceMapping(
**fake_block_device.FakeDbBlockDeviceDict({
'instance_uuid': 'fake-instance',
'source_type': 'blank',
'destination_type': 'volume',
'volume_size': 1,
'boot_index': 1}))
blank_volume2 = objects.BlockDeviceMapping(
**fake_block_device.FakeDbBlockDeviceDict({
'instance_uuid': 'fake-instance',
'source_type': 'blank',
'destination_type': 'volume',
'volume_size': 1,
'boot_index': 2}))
bdms = [blank_volume1, blank_volume2, root_volume]
def fake_attach_block_devices(bdm, *args, **kwargs):
return bdm
convert_swap.return_value = []
convert_ephemerals.return_value = []
convert_volumes.return_value = [blank_volume1, blank_volume2]
convert_snapshots.return_value = []
convert_images.return_value = [root_volume]
convert_blanks.return_value = []
attach_block_devices.side_effect = fake_attach_block_devices
get_swap.return_value = []
expected_block_device_info = {
'root_device_name': '/dev/vda',
'swap': [],
'ephemerals': [],
'block_device_mapping': bdms
}
manager = compute_manager.ComputeManager()
manager.use_legacy_block_device_info = False
block_device_info = manager._prep_block_device(self.context, instance,
bdms)
convert_swap.assert_called_once_with(bdms)
convert_ephemerals.assert_called_once_with(bdms)
convert_volumes.assert_called_once_with(bdms)
convert_snapshots.assert_called_once_with(bdms)
convert_images.assert_called_once_with(bdms)
convert_blanks.assert_called_once_with(bdms)
self.assertEqual(expected_block_device_info, block_device_info)
self.assertEqual(4, attach_block_devices.call_count)
get_swap.assert_called_once_with([])
class ComputeTestCase(BaseTestCase):
def test_wrap_instance_fault(self):
@ -6800,6 +6874,64 @@ class ComputeTestCase(BaseTestCase):
instance,
{}, bdms)
def test_default_block_device_names_with_blank_volumes(self):
instance = self._create_fake_instance()
image_meta = {}
root_volume = objects.BlockDeviceMapping(
**fake_block_device.FakeDbBlockDeviceDict({
'id': 1, 'instance_uuid': 'fake-instance',
'source_type': 'volume',
'destination_type': 'volume',
'image_id': 'fake-image-id-1',
'boot_index': 0}))
blank_volume1 = objects.BlockDeviceMapping(
**fake_block_device.FakeDbBlockDeviceDict({
'id': 2, 'instance_uuid': 'fake-instance',
'source_type': 'blank',
'destination_type': 'volume',
'boot_index': -1}))
blank_volume2 = objects.BlockDeviceMapping(
**fake_block_device.FakeDbBlockDeviceDict({
'id': 3, 'instance_uuid': 'fake-instance',
'source_type': 'blank',
'destination_type': 'volume',
'boot_index': -1}))
ephemeral = objects.BlockDeviceMapping(
**fake_block_device.FakeDbBlockDeviceDict({
'id': 4, 'instance_uuid': 'fake-instance',
'source_type': 'blank',
'destination_type': 'local'}))
swap = objects.BlockDeviceMapping(
**fake_block_device.FakeDbBlockDeviceDict({
'id': 5, 'instance_uuid': 'fake-instance',
'source_type': 'blank',
'destination_type': 'local',
'guest_format': 'swap'
}))
bdms = block_device_obj.block_device_make_list(
self.context, [root_volume, blank_volume1, blank_volume2,
ephemeral, swap])
with contextlib.nested(
mock.patch.object(self.compute, '_default_root_device_name',
return_value='/dev/vda'),
mock.patch.object(self.compute, '_instance_update'),
mock.patch.object(objects.BlockDeviceMapping, 'save'),
mock.patch.object(self.compute,
'_default_device_names_for_instance')
) as (default_root_device, instance_update, object_save,
default_device_names):
self.compute._default_block_device_names(self.context, instance,
image_meta, bdms)
default_root_device.assert_called_once_with(instance, image_meta,
bdms[0])
instance_update.assert_called_once_with(
self.context, instance['uuid'], root_device_name='/dev/vda')
self.assertTrue(object_save.called)
default_device_names.assert_called_once_with(instance,
'/dev/vda', [bdms[-2]], [bdms[-1]],
[bdm for bdm in bdms[:-2]])
def test_reserve_block_device_name(self):
instance = self._create_fake_instance_obj(
params={'root_device_name': '/dev/vda'})

View File

@ -279,6 +279,12 @@ class DefaultDeviceNamesForInstanceTestCase(test.NoDBTestCase):
'source_type': 'snapshot',
'destination_type': 'volume',
'snapshot_id': 'fake-snapshot-id-1',
'boot_index': -1}),
fake_block_device.FakeDbBlockDeviceDict(
{'id': 5, 'instance_uuid': 'fake-instance',
'device_name': '/dev/vde',
'source_type': 'blank',
'destination_type': 'volume',
'boot_index': -1})])
self.flavor = {'swap': 4}
self.instance = {'uuid': 'fake_instance', 'ephemeral_gb': 2}
@ -326,11 +332,14 @@ class DefaultDeviceNamesForInstanceTestCase(test.NoDBTestCase):
for original, new in zip(original_bdm, self.block_device_mapping):
self.assertEqual(original.device_name, new.device_name)
# Asser it defaults the missing one as expected
# Assert it defaults the missing one as expected
self.block_device_mapping[1]['device_name'] = None
self.block_device_mapping[2]['device_name'] = None
self._test_default_device_names([], [], self.block_device_mapping)
self.assertEqual(self.block_device_mapping[1]['device_name'],
'/dev/vdb')
self.assertEqual('/dev/vdb',
self.block_device_mapping[1]['device_name'])
self.assertEqual('/dev/vdc',
self.block_device_mapping[2]['device_name'])
def test_with_ephemerals(self):
# Test ephemeral gets assigned
@ -340,10 +349,13 @@ class DefaultDeviceNamesForInstanceTestCase(test.NoDBTestCase):
self.assertEqual(self.ephemerals[0]['device_name'], '/dev/vdb')
self.block_device_mapping[1]['device_name'] = None
self.block_device_mapping[2]['device_name'] = None
self._test_default_device_names(self.ephemerals, [],
self.block_device_mapping)
self.assertEqual(self.block_device_mapping[1]['device_name'],
'/dev/vdc')
self.assertEqual('/dev/vdc',
self.block_device_mapping[1]['device_name'])
self.assertEqual('/dev/vdd',
self.block_device_mapping[2]['device_name'])
def test_with_swap(self):
# Test swap only
@ -354,11 +366,14 @@ class DefaultDeviceNamesForInstanceTestCase(test.NoDBTestCase):
# Test swap and block_device_mapping
self.swap[0]['device_name'] = None
self.block_device_mapping[1]['device_name'] = None
self.block_device_mapping[2]['device_name'] = None
self._test_default_device_names([], self.swap,
self.block_device_mapping)
self.assertEqual(self.swap[0]['device_name'], '/dev/vdb')
self.assertEqual(self.block_device_mapping[1]['device_name'],
'/dev/vdc')
self.assertEqual('/dev/vdc',
self.block_device_mapping[1]['device_name'])
self.assertEqual('/dev/vdd',
self.block_device_mapping[2]['device_name'])
def test_all_together(self):
# Test swap missing
@ -379,12 +394,15 @@ class DefaultDeviceNamesForInstanceTestCase(test.NoDBTestCase):
self.swap[0]['device_name'] = None
self.ephemerals[0]['device_name'] = None
self.block_device_mapping[1]['device_name'] = None
self.block_device_mapping[2]['device_name'] = None
self._test_default_device_names(self.ephemerals,
self.swap, self.block_device_mapping)
self.assertEqual(self.ephemerals[0]['device_name'], '/dev/vdb')
self.assertEqual(self.swap[0]['device_name'], '/dev/vdc')
self.assertEqual(self.block_device_mapping[1]['device_name'],
'/dev/vdd')
self.assertEqual('/dev/vdd',
self.block_device_mapping[1]['device_name'])
self.assertEqual('/dev/vde',
self.block_device_mapping[2]['device_name'])
class UsageInfoTestCase(test.TestCase):

View File

@ -524,6 +524,16 @@ class TestBlockDeviceDict(test.NoDBTestCase):
block_device.BlockDeviceDict.from_api(api),
matchers.IsSubDictOf(new))
def test_from_api_invalid_blank_id(self):
api_dict = {'id': 1,
'source_type': 'blank',
'destination_type': 'volume',
'uuid': 'fake-volume-id-1',
'delete_on_termination': True,
'boot_index': -1}
self.assertRaises(exception.InvalidBDMFormat,
block_device.BlockDeviceDict.from_api, api_dict)
def test_legacy(self):
for legacy, new in zip(self.legacy_mapping, self.new_mapping):
self.assertThat(

View File

@ -893,6 +893,15 @@ class DefaultDeviceNamesTestCase(test.TestCase):
'disk_bus': 'virtio',
'destination_type': 'volume',
'snapshot_id': 'fake-snapshot-id-1',
'boot_index': -1})),
objects.BlockDeviceMapping(self.context,
**fake_block_device.FakeDbBlockDeviceDict(
{'id': 5, 'instance_uuid': 'fake-instance',
'device_name': '/dev/vde',
'source_type': 'blank',
'device_type': 'disk',
'disk_bus': 'virtio',
'destination_type': 'volume',
'boot_index': -1}))]
def tearDown(self):
@ -915,11 +924,14 @@ class DefaultDeviceNamesTestCase(test.TestCase):
original_bdm, self.block_device_mapping):
self.assertEqual(original.device_name, defaulted.device_name)
# Asser it defaults the missing one as expected
# Assert it defaults the missing one as expected
self.block_device_mapping[1]['device_name'] = None
self.block_device_mapping[2]['device_name'] = None
self._test_default_device_names([], [], self.block_device_mapping)
self.assertEqual('/dev/vdd',
self.block_device_mapping[1]['device_name'])
self.assertEqual('/dev/vde',
self.block_device_mapping[2]['device_name'])
def test_with_ephemerals(self):
# Test ephemeral gets assigned
@ -929,10 +941,13 @@ class DefaultDeviceNamesTestCase(test.TestCase):
self.assertEqual('/dev/vdb', self.ephemerals[0]['device_name'])
self.block_device_mapping[1]['device_name'] = None
self.block_device_mapping[2]['device_name'] = None
self._test_default_device_names(self.ephemerals, [],
self.block_device_mapping)
self.assertEqual('/dev/vdd',
self.block_device_mapping[1]['device_name'])
self.assertEqual('/dev/vde',
self.block_device_mapping[2]['device_name'])
def test_with_swap(self):
# Test swap only
@ -943,11 +958,14 @@ class DefaultDeviceNamesTestCase(test.TestCase):
# Test swap and block_device_mapping
self.swap[0]['device_name'] = None
self.block_device_mapping[1]['device_name'] = None
self.block_device_mapping[2]['device_name'] = None
self._test_default_device_names([], self.swap,
self.block_device_mapping)
self.assertEqual('/dev/vdc', self.swap[0]['device_name'])
self.assertEqual('/dev/vdd',
self.block_device_mapping[1]['device_name'])
self.assertEqual('/dev/vde',
self.block_device_mapping[2]['device_name'])
def test_all_together(self):
# Test swap missing
@ -968,9 +986,12 @@ class DefaultDeviceNamesTestCase(test.TestCase):
self.swap[0]['device_name'] = None
self.ephemerals[0]['device_name'] = None
self.block_device_mapping[1]['device_name'] = None
self.block_device_mapping[2]['device_name'] = None
self._test_default_device_names(self.ephemerals,
self.swap, self.block_device_mapping)
self.assertEqual('/dev/vdb', self.ephemerals[0]['device_name'])
self.assertEqual('/dev/vdc', self.swap[0]['device_name'])
self.assertEqual('/dev/vdd',
self.block_device_mapping[1]['device_name'])
self.assertEqual('/dev/vde',
self.block_device_mapping[2]['device_name'])

View File

@ -12,12 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import mock
from nova import block_device
from nova import context
from nova.openstack.common import jsonutils
from nova import test
from nova.tests import fake_instance
from nova.tests import matchers
from nova.virt import block_device as driver_block_device
from nova.virt import driver
@ -31,7 +34,8 @@ class TestDriverBlockDevice(test.NoDBTestCase):
'ephemeral': driver_block_device.DriverEphemeralBlockDevice,
'volume': driver_block_device.DriverVolumeBlockDevice,
'snapshot': driver_block_device.DriverSnapshotBlockDevice,
'image': driver_block_device.DriverImageBlockDevice
'image': driver_block_device.DriverImageBlockDevice,
'blank': driver_block_device.DriverBlankBlockDevice
}
swap_bdm = block_device.BlockDeviceDict(
@ -163,6 +167,34 @@ class TestDriverBlockDevice(test.NoDBTestCase):
'connection_info': {"fake": "connection_info"},
'delete_on_termination': True}
blank_bdm = block_device.BlockDeviceDict(
{'id': 6, 'instance_uuid': 'fake-instance',
'device_name': '/dev/sda2',
'delete_on_termination': True,
'volume_size': 3,
'disk_bus': 'scsi',
'device_type': 'disk',
'source_type': 'blank',
'destination_type': 'volume',
'connection_info': '{"fake": "connection_info"}',
'snapshot_id': 'fake-snapshot-id-1',
'volume_id': 'fake-volume-id-2',
'boot_index': -1})
blank_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}
blank_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)
@ -275,6 +307,15 @@ class TestDriverBlockDevice(test.NoDBTestCase):
self.assertRaises(driver_block_device._InvalidType,
self.driver_classes['image'], bdm)
def test_driver_blank_block_device(self):
self._test_driver_device('blank')
test_bdm = self.driver_classes['blank'](
self.blank_bdm)
self.assertEqual(6, test_bdm._bdm_obj.id)
self.assertEqual('fake-volume-id-2', test_bdm.volume_id)
self.assertEqual(3, test_bdm.volume_size)
def _test_volume_attach(self, driver_bdm, bdm_dict,
fake_volume, check_attach=True,
fail_check_attach=False, driver_attach=False,
@ -546,6 +587,33 @@ class TestDriverBlockDevice(test.NoDBTestCase):
self.virt_driver)
self.assertEqual(test_bdm.volume_id, 'fake-volume-id-2')
def test_blank_attach_volume(self):
no_blank_volume = self.blank_bdm.copy()
no_blank_volume['volume_id'] = None
test_bdm = self.driver_classes['blank'](no_blank_volume)
instance = fake_instance.fake_instance_obj(mock.sentinel.ctx,
**{'uuid': 'fake-uuid'})
volume_class = self.driver_classes['volume']
volume = {'id': 'fake-volume-id-2',
'display_name': 'fake-uuid-blank-vol'}
with contextlib.nested(
mock.patch.object(self.volume_api, 'create', return_value=volume),
mock.patch.object(volume_class, 'attach')
) as (vol_create, vol_attach):
test_bdm.attach(self.context, instance, self.volume_api,
self.virt_driver)
vol_create.assert_called_once_with(self.context,
test_bdm.volume_size,
'fake-uuid-blank-vol',
'')
vol_attach.assert_called_once_with(self.context, instance,
self.volume_api,
self.virt_driver,
do_check_attach=True)
self.assertEqual('fake-volume-id-2', test_bdm.volume_id)
def test_convert_block_devices(self):
converted = driver_block_device._convert_block_devices(
self.driver_classes['volume'],

View File

@ -324,6 +324,26 @@ class DriverImageBlockDevice(DriverVolumeBlockDevice):
do_check_attach=do_check_attach)
class DriverBlankBlockDevice(DriverVolumeBlockDevice):
_valid_source = 'blank'
_proxy_as_attr = set(['volume_size', 'volume_id', 'image_id'])
def attach(self, context, instance, volume_api,
virt_driver, wait_func=None, do_check_attach=True):
if not self.volume_id:
vol_name = instance.uuid + '-blank-vol'
vol = volume_api.create(context, self.volume_size, vol_name, '')
if wait_func:
wait_func(context, vol['id'])
self.volume_id = vol['id']
super(DriverBlankBlockDevice, self).attach(
context, instance, volume_api, virt_driver,
do_check_attach=do_check_attach)
def _convert_block_devices(device_type, block_device_mapping):
def _is_transformable(bdm):
try:
@ -355,6 +375,9 @@ convert_snapshots = functools.partial(_convert_block_devices,
convert_images = functools.partial(_convert_block_devices,
DriverImageBlockDevice)
convert_blanks = functools.partial(_convert_block_devices,
DriverBlankBlockDevice)
def attach_block_devices(block_device_mapping, *attach_args, **attach_kwargs):
def _log_and_attach(bdm):
@ -417,7 +440,7 @@ def get_swap(transformed_list):
_IMPLEMENTED_CLASSES = (DriverSwapBlockDevice, DriverEphemeralBlockDevice,
DriverVolumeBlockDevice, DriverSnapshotBlockDevice,
DriverImageBlockDevice)
DriverImageBlockDevice, DriverBlankBlockDevice)
def is_implemented(bdm):

View File

@ -442,6 +442,8 @@ def default_device_names(virt_type, context, instance, root_device_name,
driver_block_device.convert_volumes(
block_device_mapping) +
driver_block_device.convert_snapshots(
block_device_mapping) +
driver_block_device.convert_blanks(
block_device_mapping))
}