Enable libvirt driver to use the new BDM format

This patch makes the necessary changes in the libvirt driver to enable
it to use some of the features of the new block device mapping format.

After this patch it will be possible to set the bus, and device_type per
block device, and libvirt driver will honor these when spawning an
instance (note that attaching a volume still does not use the new data
format).

It utilizes some of the existing code in the blockinfo module to be able
to default device names (it does so by overriding the methods introduced
in I84541f8ff6e1b5978734e5def69946d014c66fdf), and also assign default
values to fields like device_type and disk_bus if it is not provided or
if it is bogus. As this implies the driver changing the block devices in
the database, a new virtapi method block_device_mapping_update is added
to accommodate this. Some of the libvirt specific code paths in the
general defaulting function in compute utils have been removed as they
are not needed since the driver now takes care of this.

Further to that, this patch modifies some of the code paths in the
libvirt driver that use the block device info directly (and not through
the blockinfo module) to be aware of the new format. Due to very nicely
factored code - there were only a few instances of this in the driver
itself.

It also overrides the libvirt driver's need_legacy_block_device_info
method to tell the compute manager to feed it the new format when
needed.

This patch concludes the blueprint: improve-block-device-handling

Change-Id: I8efd6af6706a097fb540e040a86ccbeaf131631f
This commit is contained in:
Nikola Dipanov
2013-08-14 20:58:07 +02:00
parent 3b1f7c55c1
commit f9f247ef42
11 changed files with 641 additions and 186 deletions

View File

@@ -406,6 +406,11 @@ def strip_dev(device_name):
return _dev.sub('', device_name) if device_name else device_name
def prepend_dev(device_name):
"""Make sure there is a leading '/dev/'."""
return device_name and '/dev/' + strip_dev(device_name)
_pref = re.compile('^((x?v|s)d)')

View File

@@ -412,6 +412,10 @@ class ComputeVirtAPI(virtapi.VirtAPI):
return capi.block_device_mapping_get_all_by_instance(context, instance,
legacy=legacy)
def block_device_mapping_update(self, context, bdm_id, values):
return self._compute.conductor_api.block_device_mapping_update(
context, bdm_id, values)
class ComputeManager(manager.SchedulerDependentManager):
"""Manages the running instances from creation to destruction."""

View File

@@ -135,54 +135,21 @@ def default_device_names_for_instance(instance, root_device_name,
update_function, *block_device_lists):
"""Generate missing device names for an instance."""
def _device_names(iterables):
return [bdm['device_name']
for bdm in itertools.chain(*iterables) if bdm['device_name']]
dev_list = _device_names(block_device_lists)
dev_list = [bdm['device_name']
for bdm in itertools.chain(*block_device_lists)
if bdm['device_name']]
if root_device_name not in dev_list:
dev_list.append(root_device_name)
inst_type = flavors.extract_flavor(instance)
is_libvirt = driver.compute_driver_matches('libvirt.LibvirtDriver')
libvirt_default_ephemerals = []
bdm_named_lists = zip(('ephemerals', 'swap', 'block_device_mapping'),
block_device_lists)
for name, bdm_list in bdm_named_lists:
# Libvirt will create a default ephemeral if instance allows
# and was not overridden.
if (is_libvirt and name == 'ephemerals' and not bdm_list and
instance['ephemeral_gb'] > 0):
default_eph = get_next_device_name(instance,
[root_device_name],
root_device_name)
if default_eph not in dev_list:
dev_list.append(default_eph)
libvirt_default_ephemerals.append(default_eph)
# Libvirt will create a default swap if it's in instance type
# and it was not supplied or overridden
if (is_libvirt and name == 'swap' and not bdm_list and
inst_type['swap'] > 0):
ephemerals = (_device_names(bdm_named_lists[0][1]) or
libvirt_default_ephemerals)
default_swap = get_next_device_name(instance,
[root_device_name] + ephemerals, root_device_name)
if default_swap not in dev_list:
dev_list.append(default_swap)
for bdm in bdm_list:
dev = bdm.get('device_name')
if not dev:
dev = get_next_device_name(instance, dev_list,
root_device_name)
bdm['device_name'] = dev
if update_function:
update_function(bdm)
dev_list.append(dev)
for bdm in itertools.chain(*block_device_lists):
dev = bdm.get('device_name')
if not dev:
dev = get_next_device_name(instance, dev_list,
root_device_name)
bdm['device_name'] = dev
if update_function:
update_function(bdm)
dev_list.append(dev)
def get_next_device_name(instance, device_name_list,
@@ -217,11 +184,6 @@ def get_next_device_name(instance, device_name_list,
if driver.compute_driver_matches('xenapi.XenAPIDriver'):
prefix = '/dev/xvd'
# NOTE(xqueralt): This can be removed when we have libvirt
# defaulting it's own device names.
if driver.compute_driver_matches('libvirt.LibvirtDriver'):
prefix = '/dev/vd'
if req_prefix != prefix:
LOG.debug(_("Using %(prefix)s instead of %(req_prefix)s"),
{'prefix': prefix, 'req_prefix': req_prefix})

View File

@@ -355,40 +355,6 @@ class DefaultDeviceNamesForInstanceTestCase(test.TestCase):
self.assertEquals(self.block_device_mapping[1]['device_name'],
'/dev/vdd')
def test_libvirt_default_eph(self):
self.block_device_mapping[1]['device_name'] = None
self._test_default_device_names(None, [], [],
self.block_device_mapping)
self.assertEquals(self.block_device_mapping[1]['device_name'],
'/dev/vdb')
# Test that default eph will be taken into account on libvirt
self.is_libvirt = True
self.instance_type['swap'] = 0
self.block_device_mapping[1]['device_name'] = None
self._test_default_device_names(None, [], [],
self.block_device_mapping)
self.assertEquals(self.block_device_mapping[1]['device_name'],
'/dev/vdc')
def test_libvirt_default_swap(self):
# Test that default swap will be taken into account on libvirt
self.is_libvirt = True
self.instance['ephemeral_gb'] = 0
self.block_device_mapping[1]['device_name'] = None
self._test_default_device_names(None, [], [],
self.block_device_mapping)
self.assertEquals(self.block_device_mapping[1]['device_name'],
'/dev/vdc')
def test_libvirt_default_swap_ephemeral(self):
self.is_libvirt = True
self.block_device_mapping[1]['device_name'] = None
self._test_default_device_names(None, [], [],
self.block_device_mapping)
self.assertEquals(self.block_device_mapping[1]['device_name'],
'/dev/vdd')
def test_update_fn_gets_called(self):
def _update(bdm):
self.update_called = True

View File

@@ -79,6 +79,10 @@ class VirtAPIBaseTest(test.TestCase, test.APICoverage):
self.assertExpected('block_device_mapping_get_all_by_instance',
{'uuid': 'fake_uuid'}, legacy=False)
def test_block_device_mapping_update(self):
self.assertExpected('block_device_mapping_update',
'fake_bdm', 'fake_values')
class FakeVirtAPITest(VirtAPIBaseTest):

View File

@@ -1202,8 +1202,8 @@ class LibvirtConnTestCase(test.TestCase):
def test_xml_disk_bus_ide_and_virtio(self):
swap = {'device_name': '/dev/vdc',
'swap_size': 1}
ephemerals = [{'num': 0,
'virtual_name': 'ephemeral0',
ephemerals = [{'device_type': 'disk',
'disk_bus': 'virtio',
'device_name': '/dev/vdb',
'size': 1}]
block_device_info = {
@@ -3106,7 +3106,10 @@ class LibvirtConnTestCase(test.TestCase):
block_device_info = {'root_device_name': '/dev/vda',
'block_device_mapping': [
{'mount_device': 'vda'}]}
{'mount_device': 'vda',
'boot_index': 0}
]
}
# Volume-backed instance created without image
instance_ref = self.test_instance
@@ -4777,6 +4780,54 @@ class LibvirtConnTestCase(test.TestCase):
"""
self._test_attach_detach_interface_get_config("detach_interface")
def test_default_root_device_name(self):
instance = {'uuid': 'fake_instance'}
image_meta = {'id': 'fake'}
root_bdm = {'source_type': 'image',
'detination_type': 'volume',
'image_id': 'fake_id'}
self.flags(libvirt_type='fake_libvirt_type')
self.mox.StubOutWithMock(blockinfo, 'get_disk_bus_for_device_type')
self.mox.StubOutWithMock(blockinfo, 'get_root_info')
blockinfo.get_disk_bus_for_device_type('fake_libvirt_type',
image_meta,
'disk').InAnyOrder().\
AndReturn('virtio')
blockinfo.get_disk_bus_for_device_type('fake_libvirt_type',
image_meta,
'cdrom').InAnyOrder().\
AndReturn('ide')
blockinfo.get_root_info('fake_libvirt_type',
image_meta, root_bdm,
'virtio', 'ide').AndReturn({'dev': 'vda'})
self.mox.ReplayAll()
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
self.assertEqual(conn.default_root_device_name(instance, image_meta,
root_bdm), '/dev/vda')
def test_default_device_names_for_instance(self):
instance = {'uuid': 'fake_instance'}
root_device_name = '/dev/vda'
ephemerals = [{'device_name': 'vdb'}]
swap = [{'device_name': 'vdc'}]
block_device_mapping = [{'device_name': 'vdc'}]
self.flags(libvirt_type='fake_libvirt_type')
self.mox.StubOutWithMock(blockinfo, 'default_device_names')
blockinfo.default_device_names('fake_libvirt_type', instance,
root_device_name, mox.IgnoreArg(),
ephemerals, swap, block_device_mapping)
self.mox.ReplayAll()
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
conn.default_device_names_for_instance(instance, root_device_name,
ephemerals, swap,
block_device_mapping)
class HostStateTestCase(test.TestCase):

View File

@@ -15,6 +15,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import copy
import mock
from nova import block_device
from nova.compute import flavors
from nova import context
@@ -22,6 +26,7 @@ from nova import db
from nova import exception
from nova import test
import nova.tests.image.fake
from nova.tests import matchers
from nova.virt.libvirt import blockinfo
@@ -53,14 +58,10 @@ class LibvirtBlockInfoTest(test.TestCase):
def test_volume_in_mapping(self):
swap = {'device_name': '/dev/sdb',
'swap_size': 1}
ephemerals = [{'num': 0,
'virtual_name': 'ephemeral0',
'device_name': '/dev/sdc1',
'size': 1},
{'num': 2,
'virtual_name': 'ephemeral2',
'device_name': '/dev/sdd',
'size': 1}]
ephemerals = [{'device_type': 'disk', 'guest_format': 'ext3',
'device_name': '/dev/sdc1', 'size': 10},
{'disk_bus': 'ide', 'guest_format': None,
'device_name': '/dev/sdd', 'size': 10}]
block_device_mapping = [{'mount_device': '/dev/sde',
'device_path': 'fake_device'},
{'mount_device': '/dev/sdf',
@@ -301,11 +302,11 @@ class LibvirtBlockInfoTest(test.TestCase):
block_device_info = {
'ephemerals': [
{'num': 0, 'virtual_name': 'ephemeral0',
{'device_type': 'disk', 'guest_format': 'ext3',
'device_name': '/dev/vdb', 'size': 10},
{'num': 1, 'virtual_name': 'ephemeral1',
{'disk_bus': 'ide', 'guest_format': None,
'device_name': '/dev/vdc', 'size': 10},
{'num': 2, 'virtual_name': 'ephemeral2',
{'device_type': 'floppy',
'device_name': '/dev/vdd', 'size': 10},
]
}
@@ -315,9 +316,10 @@ class LibvirtBlockInfoTest(test.TestCase):
expect = {
'disk': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'},
'disk.eph0': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'},
'disk.eph1': {'bus': 'virtio', 'dev': 'vdc', 'type': 'disk'},
'disk.eph2': {'bus': 'virtio', 'dev': 'vdd', 'type': 'disk'},
'disk.eph0': {'bus': 'virtio', 'dev': 'vdb',
'type': 'disk', 'format': 'ext3'},
'disk.eph1': {'bus': 'ide', 'dev': 'vdc', 'type': 'disk'},
'disk.eph2': {'bus': 'virtio', 'dev': 'vdd', 'type': 'floppy'},
'disk.swap': {'bus': 'virtio', 'dev': 'vde', 'type': 'disk'},
'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}
}
@@ -353,6 +355,8 @@ class LibvirtBlockInfoTest(test.TestCase):
'block_device_mapping': [
{'connection_info': "fake",
'mount_device': "/dev/vda",
'boot_index': 0,
'device_type': 'disk',
'delete_on_termination': True},
]
}
@@ -376,6 +380,7 @@ class LibvirtBlockInfoTest(test.TestCase):
'block_device_mapping': [
{'connection_info': "fake",
'mount_device': "/dev/vdb",
'boot_index': -1,
'delete_on_termination': True},
]
}
@@ -399,12 +404,17 @@ class LibvirtBlockInfoTest(test.TestCase):
'block_device_mapping': [
{'connection_info': "fake",
'mount_device': "/dev/vda",
'boot_index': 0,
'disk_bus': 'scsi',
'delete_on_termination': True},
{'connection_info': "fake",
'mount_device': "/dev/vdb",
'boot_index': -1,
'delete_on_termination': True},
{'connection_info': "fake",
'mount_device': "/dev/vdc",
'boot_index': -1,
'device_type': 'cdrom',
'delete_on_termination': True},
]
}
@@ -413,10 +423,10 @@ class LibvirtBlockInfoTest(test.TestCase):
block_device_info)
expect = {
'/dev/vda': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'},
'/dev/vda': {'bus': 'scsi', 'dev': 'vda', 'type': 'disk'},
'/dev/vdb': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'},
'/dev/vdc': {'bus': 'virtio', 'dev': 'vdc', 'type': 'disk'},
'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}
'/dev/vdc': {'bus': 'virtio', 'dev': 'vdc', 'type': 'cdrom'},
'root': {'bus': 'scsi', 'dev': 'vda', 'type': 'disk'}
}
self.assertEqual(mapping, expect)
@@ -430,9 +440,9 @@ class LibvirtBlockInfoTest(test.TestCase):
'swap': {'device_name': '/dev/vdy',
'swap_size': 10},
'ephemerals': [
{'num': 0, 'virtual_name': 'ephemeral0',
{'device_type': 'disk', 'guest_format': 'ext3',
'device_name': '/dev/vdb', 'size': 10},
{'num': 1, 'virtual_name': 'ephemeral1',
{'disk_bus': 'ide', 'guest_format': None,
'device_name': '/dev/vdc', 'size': 10},
],
'block_device_mapping': [
@@ -448,13 +458,52 @@ class LibvirtBlockInfoTest(test.TestCase):
expect = {
'disk': {'bus': 'virtio', 'dev': 'vdf', 'type': 'disk'},
'/dev/vda': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'},
'disk.eph0': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'},
'disk.eph1': {'bus': 'virtio', 'dev': 'vdc', 'type': 'disk'},
'disk.eph0': {'bus': 'virtio', 'dev': 'vdb',
'type': 'disk', 'format': 'ext3'},
'disk.eph1': {'bus': 'ide', 'dev': 'vdc', 'type': 'disk'},
'disk.swap': {'bus': 'virtio', 'dev': 'vdy', 'type': 'disk'},
'root': {'bus': 'virtio', 'dev': 'vdf', 'type': 'disk'}
}
self.assertEqual(mapping, expect)
def test_get_disk_mapping_updates_original(self):
user_context = context.RequestContext(self.user_id, self.project_id)
instance_ref = db.instance_create(user_context, self.test_instance)
block_device_info = {
'root_device_name': '/dev/vda',
'swap': {'device_name': '/dev/vdb',
'device_type': 'really_lame_type',
'swap_size': 10},
'ephemerals': [{'disk_bus': 'no_such_bus',
'device_type': 'yeah_right',
'device_name': '/dev/vdc', 'size': 10}],
'block_device_mapping': [
{'connection_info': "fake",
'mount_device': None,
'device_type': 'lawnmower',
'delete_on_termination': True}]
}
expected_swap = {'device_name': '/dev/vdb', 'disk_bus': 'virtio',
'device_type': 'disk', 'swap_size': 10}
expected_ephemeral = {'disk_bus': 'virtio',
'device_type': 'disk',
'device_name': '/dev/vdc', 'size': 10}
expected_bdm = {'connection_info': "fake",
'mount_device': '/dev/vdd',
'device_type': 'disk',
'disk_bus': 'virtio',
'delete_on_termination': True}
blockinfo.get_disk_mapping("kvm", instance_ref,
"virtio", "ide", block_device_info)
self.assertEqual(expected_swap, block_device_info['swap'])
self.assertEqual(expected_ephemeral,
block_device_info['ephemerals'][0])
self.assertEqual(expected_bdm,
block_device_info['block_device_mapping'][0])
def test_get_disk_bus(self):
bus = blockinfo.get_disk_bus_for_device_type('kvm')
self.assertEqual(bus, 'virtio')
@@ -503,3 +552,240 @@ class LibvirtBlockInfoTest(test.TestCase):
self.flags(config_drive_format='test')
self.assertRaises(exception.ConfigDriveUnknownFormat,
blockinfo.get_config_drive_type)
def test_get_info_from_bdm(self):
bdms = [{'device_name': '/dev/vds', 'device_type': 'disk',
'disk_bus': 'usb', 'swap_size': 4},
{'device_type': 'disk', 'guest_format': 'ext3',
'device_name': '/dev/vdb', 'size': 2},
{'disk_bus': 'ide', 'guest_format': None,
'device_name': '/dev/vdc', 'size': 3},
{'connection_info': "fake",
'mount_device': "/dev/sdr",
'disk_bus': 'lame_bus',
'device_type': 'cdrom',
'delete_on_termination': True},
{'connection_info': "fake",
'mount_device': "/dev/vdo",
'disk_bus': 'scsi',
'device_type': 'lame_type',
'delete_on_termination': True}]
expected = [{'dev': 'vds', 'type': 'disk', 'bus': 'usb'},
{'dev': 'vdb', 'type': 'disk',
'bus': 'virtio', 'format': 'ext3'},
{'dev': 'vdc', 'type': 'disk', 'bus': 'ide'},
{'dev': 'sdr', 'type': 'cdrom', 'bus': 'scsi'},
{'dev': 'vdo', 'type': 'disk', 'bus': 'scsi'}]
for bdm, expected in zip(bdms, expected):
self.assertEqual(expected,
blockinfo.get_info_from_bdm('kvm', bdm, {}))
# Test that passed bus and type are considered
bdm = {'device_name': '/dev/vda'}
expected = {'dev': 'vda', 'type': 'disk', 'bus': 'ide'}
self.assertEqual(
expected, blockinfo.get_info_from_bdm('kvm', bdm, {},
disk_bus='ide',
dev_type='disk'))
# Test that lame bus values are defaulted properly
bdm = {'disk_bus': 'lame_bus', 'device_type': 'cdrom'}
with mock.patch.object(blockinfo,
'get_disk_bus_for_device_type',
return_value='ide') as get_bus:
blockinfo.get_info_from_bdm('kvm', bdm, {})
get_bus.assert_called_once_with('kvm', None, 'cdrom')
# Test that missing device is defaulted as expected
bdm = {'disk_bus': 'ide', 'device_type': 'cdrom'}
expected = {'dev': 'vdd', 'type': 'cdrom', 'bus': 'ide'}
mapping = {'root': {'dev': 'vda'}}
with mock.patch.object(blockinfo,
'find_disk_dev_for_disk_bus',
return_value='vdd') as find_dev:
got = blockinfo.get_info_from_bdm(
'kvm', bdm, mapping, assigned_devices=['vdb', 'vdc'])
find_dev.assert_called_once_with(
{'root': {'dev': 'vda'},
'vdb': {'dev': 'vdb'},
'vdc': {'dev': 'vdc'}}, 'ide')
self.assertEqual(expected, got)
@mock.patch('nova.virt.libvirt.blockinfo.find_disk_dev_for_disk_bus',
return_value='vda')
@mock.patch('nova.virt.libvirt.blockinfo.get_disk_bus_for_disk_dev',
return_value='virtio')
def test_get_root_info_no_bdm(self, mock_get_bus, mock_find_dev):
blockinfo.get_root_info('kvm', None, None, 'virtio', 'ide')
mock_find_dev.assert_called_once_with({}, 'virtio')
blockinfo.get_root_info('kvm', None, None, 'virtio', 'ide',
root_device_name='/dev/vda')
mock_get_bus.assert_called_once_with('kvm', '/dev/vda')
@mock.patch('nova.virt.libvirt.blockinfo.get_info_from_bdm')
def test_get_root_info_bdm(self, mock_get_info):
root_bdm = {'mount_device': '/dev/vda',
'disk_bus': 'scsi',
'device_type': 'disk'}
# No root_device_name
blockinfo.get_root_info('kvm', None, root_bdm, 'virtio', 'ide')
mock_get_info.assert_called_once_with('kvm', root_bdm, {})
mock_get_info.reset_mock()
# Both device names
blockinfo.get_root_info('kvm', None, root_bdm, 'virtio', 'ide',
root_device_name='sda')
mock_get_info.assert_called_once_with('kvm', root_bdm, {})
mock_get_info.reset_mock()
# Missing device names
del root_bdm['mount_device']
blockinfo.get_root_info('kvm', None, root_bdm, 'virtio', 'ide',
root_device_name='sda')
mock_get_info.assert_called_once_with('kvm',
{'device_name': 'sda',
'disk_bus': 'scsi',
'device_type': 'disk'}, {})
class DefaultDeviceNamesTestCase(test.TestCase):
def setUp(self):
super(DefaultDeviceNamesTestCase, self).setUp()
self.instance = {
'uuid': '32dfcb37-5af1-552b-357c-be8c3aa38310',
'memory_kb': '1024000',
'basepath': '/some/path',
'bridge_name': 'br100',
'vcpus': 2,
'project_id': 'fake',
'bridge': 'br101',
'image_ref': '155d900f-4e14-4e4c-a73d-069cbf4541e6',
'root_gb': 10,
'ephemeral_gb': 20,
'instance_type_id': 2}
self.root_device_name = '/dev/vda'
self.libvirt_type = 'kvm'
self.instance_type = {'swap': 4}
self.patcher = mock.patch('nova.compute.flavors.extract_flavor',
return_value=self.instance_type)
self.patcher.start()
self.ephemerals = [
{'id': 1, 'instance_uuid': 'fake-instance',
'device_name': '/dev/vdb',
'source_type': 'blank',
'destination_type': 'local',
'device_type': 'disk',
'disk_bus': 'virtio',
'delete_on_termination': True,
'guest_format': None,
'volume_size': 1,
'boot_index': -1}]
self.swap = [
{'id': 2, 'instance_uuid': 'fake-instance',
'device_name': '/dev/vdc',
'source_type': 'blank',
'destination_type': 'local',
'device_type': 'disk',
'disk_bus': 'virtio',
'delete_on_termination': True,
'guest_format': 'swap',
'volume_size': 1,
'boot_index': -1}]
self.block_device_mapping = [
{'id': 3, 'instance_uuid': 'fake-instance',
'device_name': '/dev/vda',
'source_type': 'volume',
'destination_type': 'volume',
'device_type': 'disk',
'disk_bus': 'virtio',
'volume_id': 'fake-volume-id-1',
'boot_index': 0},
{'id': 4, 'instance_uuid': 'fake-instance',
'device_name': '/dev/vdd',
'source_type': 'snapshot',
'device_type': 'disk',
'disk_bus': 'virtio',
'destination_type': 'volume',
'snapshot_id': 'fake-snapshot-id-1',
'boot_index': -1}]
def tearDown(self):
super(DefaultDeviceNamesTestCase, self).tearDown()
self.patcher.stop()
def _test_default_device_names(self, update_function, *block_device_lists):
blockinfo.default_device_names(self.libvirt_type, self.instance,
self.root_device_name,
update_function, *block_device_lists)
def test_only_block_device_mapping(self):
# Test no-op
original_bdm = copy.deepcopy(self.block_device_mapping)
self._test_default_device_names(None, [], [],
self.block_device_mapping)
self.assertThat(original_bdm,
matchers.DictListMatches(self.block_device_mapping))
# Asser it defaults the missing one as expected
self.block_device_mapping[1]['device_name'] = None
self._test_default_device_names(None, [], [],
self.block_device_mapping)
self.assertEquals(self.block_device_mapping[1]['device_name'],
'/dev/vdd')
def test_with_ephemerals(self):
# Test ephemeral gets assigned
self.ephemerals[0]['device_name'] = None
self._test_default_device_names(None, self.ephemerals, [],
self.block_device_mapping)
self.assertEquals(self.ephemerals[0]['device_name'], '/dev/vdb')
self.block_device_mapping[1]['device_name'] = None
self._test_default_device_names(None, self.ephemerals, [],
self.block_device_mapping)
self.assertEquals(self.block_device_mapping[1]['device_name'],
'/dev/vdd')
def test_with_swap(self):
# Test swap only
self.swap[0]['device_name'] = None
self._test_default_device_names(None, [], self.swap, [])
self.assertEquals(self.swap[0]['device_name'], '/dev/vdc')
# Test swap and block_device_mapping
self.swap[0]['device_name'] = None
self.block_device_mapping[1]['device_name'] = None
self._test_default_device_names(None, [], self.swap,
self.block_device_mapping)
self.assertEquals(self.swap[0]['device_name'], '/dev/vdc')
self.assertEquals(self.block_device_mapping[1]['device_name'],
'/dev/vdd')
def test_all_together(self):
# Test swap missing
self.swap[0]['device_name'] = None
self._test_default_device_names(None, self.ephemerals,
self.swap, self.block_device_mapping)
self.assertEquals(self.swap[0]['device_name'], '/dev/vdc')
# Test swap and eph missing
self.swap[0]['device_name'] = None
self.ephemerals[0]['device_name'] = None
self._test_default_device_names(None, self.ephemerals,
self.swap, self.block_device_mapping)
self.assertEquals(self.ephemerals[0]['device_name'], '/dev/vdb')
self.assertEquals(self.swap[0]['device_name'], '/dev/vdc')
# Test all missing
self.swap[0]['device_name'] = None
self.ephemerals[0]['device_name'] = None
self.block_device_mapping[1]['device_name'] = None
self._test_default_device_names(None, self.ephemerals,
self.swap, self.block_device_mapping)
self.assertEquals(self.ephemerals[0]['device_name'], '/dev/vdb')
self.assertEquals(self.swap[0]['device_name'], '/dev/vdc')
self.assertEquals(self.block_device_mapping[1]['device_name'],
'/dev/vdd')

View File

@@ -499,3 +499,6 @@ class FakeVirtAPI(virtapi.VirtAPI):
if legacy:
bdms = block_device.legacy_mapping(bdms)
return bdms
def block_device_mapping_update(self, context, bdm_id, values):
return db.block_device_mapping_update(context, bdm_id, values)

View File

@@ -57,20 +57,27 @@ variables / types used
(disk bus, disk dev, device type)
and possibly these optional fields: ('format',)
* 'disk_bus': the guest bus type ('ide', 'virtio', 'scsi', etc)
* 'disk_dev': the device name 'vda', 'hdc', 'sdf', 'xvde' etc
* 'device_type': type of device eg 'disk', 'cdrom', 'floppy'
* 'format': Which format to apply to the device if applicable
"""
import itertools
from oslo.config import cfg
from nova import block_device
from nova.compute import flavors
from nova import exception
from nova.openstack.common.gettextutils import _
from nova.virt import block_device as driver_block_device
from nova.virt import configdrive
from nova.virt import driver
@@ -78,6 +85,9 @@ from nova.virt import driver
CONF = cfg.CONF
SUPPORTED_DEVICE_TYPES = ('disk', 'cdrom', 'floppy')
def has_disk_dev(mapping, disk_dev):
"""Determine if a disk device name has already been used.
@@ -291,8 +301,8 @@ def get_next_disk_info(mapping, disk_bus,
'type': device_type}
def get_eph_disk(ephemeral):
return 'disk.eph' + str(ephemeral['num'])
def get_eph_disk(index):
return 'disk.eph' + str(index)
def get_config_drive_type():
@@ -315,6 +325,140 @@ def get_config_drive_type():
return config_drive_type
def get_info_from_bdm(virt_type, bdm, mapping={}, disk_bus=None,
dev_type=None, allowed_types=None,
assigned_devices=None):
allowed_types = allowed_types or SUPPORTED_DEVICE_TYPES
device_name = block_device.strip_dev(get_device_name(bdm))
bdm_type = bdm.get('device_type') or dev_type
if bdm_type not in allowed_types:
bdm_type = 'disk'
bdm_bus = bdm.get('disk_bus') or disk_bus
if not is_disk_bus_valid_for_virt(virt_type, bdm_bus):
if device_name:
bdm_bus = get_disk_bus_for_disk_dev(virt_type, device_name)
else:
bdm_bus = get_disk_bus_for_device_type(virt_type, None, bdm_type)
if not device_name:
if assigned_devices:
padded_mapping = dict((dev, {'dev': dev})
for dev in assigned_devices)
padded_mapping.update(mapping)
else:
padded_mapping = mapping
device_name = find_disk_dev_for_disk_bus(padded_mapping, bdm_bus)
bdm_info = {'bus': bdm_bus,
'dev': device_name,
'type': bdm_type}
bdm_format = bdm.get('guest_format')
if bdm_format:
bdm_info.update({'format': bdm_format})
return bdm_info
def get_device_name(bdm):
"""Get the device name if present regardless of the bdm format."""
return bdm.get('device_name') or bdm.get('mount_device')
def get_root_info(virt_type, image_meta, root_bdm, disk_bus, cdrom_bus,
root_device_name=None):
# NOTE (ndipanov): This is a hack to avoid considering an image
# BDM with local target, as we don't support them
# yet. Only aplies when passed non-driver format
no_root_bdm = (not root_bdm or (
root_bdm.get('source_type') == 'image' and
root_bdm.get('destination_type') == 'local'))
if no_root_bdm:
if (image_meta and image_meta.get('disk_format') == 'iso'):
root_device_bus = cdrom_bus
root_device_type = 'cdrom'
else:
root_device_bus = disk_bus
root_device_type = 'disk'
if root_device_name:
root_device_bus = get_disk_bus_for_disk_dev(virt_type,
root_device_name)
else:
root_device_name = find_disk_dev_for_disk_bus({}, root_device_bus)
return {'bus': root_device_bus,
'type': root_device_type,
'dev': block_device.strip_dev(root_device_name)}
else:
if not get_device_name(root_bdm) and root_device_name:
root_bdm = root_bdm.copy()
root_bdm['device_name'] = root_device_name
return get_info_from_bdm(virt_type, root_bdm, {})
def default_device_names(virt_type, instance, root_device_name,
update_func, ephemerals, swap, block_device_mapping):
block_device_info = {
'root_device_name': root_device_name,
'swap': driver_block_device.get_swap(
driver_block_device.convert_swap(swap)),
'ephemerals': driver_block_device.convert_ephemerals(ephemerals),
'block_device_mapping': (
driver_block_device.convert_volumes(
block_device_mapping) +
driver_block_device.convert_snapshots(
block_device_mapping))
}
devices = dict((bdm.get('id'), bdm) for bdm in
itertools.chain(ephemerals, swap, block_device_mapping))
get_disk_info(virt_type, instance, block_device_info)
for driver_bdm in itertools.chain(block_device_info['ephemerals'],
[block_device_info['swap']] if
block_device_info['swap'] else [],
block_device_info['block_device_mapping']):
if driver_bdm.id in devices:
bdm = devices[driver_bdm.id]
# NOTE (ndipanov): We may have chosen different values
# for bus and type so update those along with device name
bdm['device_name'] = get_device_name(driver_bdm)
bdm['disk_bus'] = driver_bdm['disk_bus']
# Swap does not have device type in driver format
bdm['device_type'] = driver_bdm.get('device_type', 'disk')
if update_func:
update_func(bdm)
def has_default_ephemeral(instance, disk_bus, block_device_info, mapping):
ephemerals = driver.block_device_info_get_ephemerals(block_device_info)
if instance['ephemeral_gb'] <= 0 or ephemerals:
return None
else:
info = get_next_disk_info(mapping, disk_bus)
if block_device.volume_in_mapping(info['dev'], block_device_info):
return None
return info
def update_bdm(bdm, info):
device_name_field = ('device_name'
if 'device_name' in bdm
else 'mount_device')
# Do not update the device name if it was already present
bdm.update(dict(zip((device_name_field,
'disk_bus', 'device_type'),
((bdm.get(device_name_field) or
block_device.prepend_dev(info['dev'])),
info['bus'], info['type']))))
def get_disk_mapping(virt_type, instance,
disk_bus, cdrom_bus,
block_device_info=None,
@@ -332,6 +476,13 @@ def get_disk_mapping(virt_type, instance,
mapping = {}
pre_assigned_device_names = \
[block_device.strip_dev(get_device_name(bdm)) for bdm in itertools.chain(
driver.block_device_info_get_ephemerals(block_device_info),
[driver.block_device_info_get_swap(block_device_info)],
driver.block_device_info_get_mapping(block_device_info))
if get_device_name(bdm)]
if virt_type == "lxc":
# NOTE(zul): This information is not used by the libvirt driver
# however we need to populate mapping so the image can be
@@ -360,60 +511,49 @@ def get_disk_mapping(virt_type, instance,
return mapping
if image_meta and image_meta.get('disk_format') == 'iso':
root_disk_bus = cdrom_bus
root_device_type = 'cdrom'
else:
root_disk_bus = disk_bus
root_device_type = 'disk'
try:
root_bdm = (bdm for bdm in
driver.block_device_info_get_mapping(block_device_info)
if bdm.get('boot_index') == 0).next()
except StopIteration:
# NOTE (ndipanov): This happens when we boot from image as
# there is no driver represenation of local targeted images
# and they will not be in block_device_info list.
root_bdm = None
root_device_name = block_device.strip_dev(
driver.block_device_info_get_root(block_device_info))
root_info = get_root_info(virt_type, image_meta, root_bdm,
disk_bus, cdrom_bus, root_device_name)
root_device_name = driver.block_device_info_get_root(block_device_info)
if root_device_name is not None:
root_device = block_device.strip_dev(root_device_name)
root_info = {'bus': get_disk_bus_for_disk_dev(virt_type,
root_device),
'dev': root_device,
'type': root_device_type}
else:
root_info = get_next_disk_info(mapping,
root_disk_bus,
root_device_type)
mapping['root'] = root_info
if not block_device.volume_in_mapping(root_info['dev'],
block_device_info):
# NOTE (ndipanov): This implicitely relies on image->local BDMs not
# being considered in the driver layer - so missing
# bdm with boot_index 0 means - use image, unless it was
# overriden. This can happen when using legacy syntax and
# no root_device_name is set on the instance.
if not root_bdm and not block_device.volume_in_mapping(root_info['dev'],
block_device_info):
mapping['disk'] = root_info
eph_info = get_next_disk_info(mapping,
disk_bus)
ephemeral_device = False
if not (block_device.volume_in_mapping(eph_info['dev'],
block_device_info) or
0 in [eph['num'] for eph in
driver.block_device_info_get_ephemerals(
block_device_info)]):
if instance['ephemeral_gb'] > 0:
ephemeral_device = True
default_eph = has_default_ephemeral(instance, disk_bus, block_device_info,
mapping)
if default_eph:
mapping['disk.local'] = default_eph
if ephemeral_device:
mapping['disk.local'] = eph_info
for eph in driver.block_device_info_get_ephemerals(
block_device_info):
disk_dev = block_device.strip_dev(eph['device_name'])
disk_bus = get_disk_bus_for_disk_dev(virt_type, disk_dev)
mapping[get_eph_disk(eph)] = {'bus': disk_bus,
'dev': disk_dev,
'type': 'disk'}
for idx, eph in enumerate(driver.block_device_info_get_ephemerals(
block_device_info)):
eph_info = get_info_from_bdm(
virt_type, eph, mapping, disk_bus,
assigned_devices=pre_assigned_device_names)
mapping[get_eph_disk(idx)] = eph_info
update_bdm(eph, eph_info)
swap = driver.block_device_info_get_swap(block_device_info)
if driver.swap_is_usable(swap):
disk_dev = block_device.strip_dev(swap['device_name'])
disk_bus = get_disk_bus_for_disk_dev(virt_type, disk_dev)
mapping['disk.swap'] = {'bus': disk_bus,
'dev': disk_dev,
'type': 'disk'}
if swap and swap.get('swap_size', 0) > 0:
swap_info = get_info_from_bdm(virt_type, swap, mapping, disk_bus)
mapping['disk.swap'] = swap_info
update_bdm(swap, swap_info)
elif inst_type['swap'] > 0:
swap_info = get_next_disk_info(mapping,
disk_bus)
@@ -425,12 +565,11 @@ def get_disk_mapping(virt_type, instance,
block_device_info)
for vol in block_device_mapping:
disk_dev = vol['mount_device'].rpartition("/")[2]
disk_bus = get_disk_bus_for_disk_dev(virt_type, disk_dev)
mapping[vol['mount_device']] = {'bus': disk_bus,
'dev': disk_dev,
'type': 'disk'}
vol_info = get_info_from_bdm(
virt_type, vol, mapping,
assigned_devices=pre_assigned_device_names)
mapping[block_device.prepend_dev(vol_info['dev'])] = vol_info
update_bdm(vol, vol_info)
if configdrive.required_by(instance):
device_type = get_config_drive_type()

View File

@@ -2311,13 +2311,14 @@ class LibvirtDriver(driver.ComputeDriver):
size=size,
ephemeral_size=ephemeral_gb)
for eph in driver.block_device_info_get_ephemerals(block_device_info):
for idx, eph in enumerate(driver.block_device_info_get_ephemerals(
block_device_info)):
fn = functools.partial(self._create_ephemeral,
fs_label='ephemeral%d' % eph['num'],
fs_label='ephemeral%d' % idx,
os_type=instance["os_type"])
size = eph['size'] * 1024 * 1024 * 1024
fname = "ephemeral_%s_%s" % (eph['size'], os_type_with_default)
image(blockinfo.get_eph_disk(eph)).cache(
image(blockinfo.get_eph_disk(idx)).cache(
fetch_func=fn,
filename=fname,
size=size,
@@ -2565,13 +2566,14 @@ class LibvirtDriver(driver.ComputeDriver):
self.virtapi.instance_update(
nova_context.get_admin_context(), instance['uuid'],
{'default_ephemeral_device':
'/dev/' + disklocal.target_dev})
block_device.prepend_dev(disklocal.target_dev)})
for eph in driver.block_device_info_get_ephemerals(
block_device_info):
for idx, eph in enumerate(
driver.block_device_info_get_ephemerals(
block_device_info)):
diskeph = self.get_guest_disk_config(
instance,
blockinfo.get_eph_disk(eph),
blockinfo.get_eph_disk(idx),
disk_mapping, inst_type)
devices.append(diskeph)
@@ -2583,11 +2585,13 @@ class LibvirtDriver(driver.ComputeDriver):
devices.append(diskswap)
self.virtapi.instance_update(
nova_context.get_admin_context(), instance['uuid'],
{'default_swap_device': '/dev/' + diskswap.target_dev})
{'default_swap_device': block_device.prepend_dev(
diskswap.target_dev)})
for vol in block_device_mapping:
connection_info = vol['connection_info']
info = disk_mapping[vol['mount_device']]
vol_dev = block_device.prepend_dev(vol['mount_device'])
info = disk_mapping[vol_dev]
cfg = self.volume_driver_method('connect_volume',
connection_info,
info)
@@ -2652,8 +2656,9 @@ class LibvirtDriver(driver.ComputeDriver):
guest.cpu = self.get_guest_cpu_config()
if 'root' in disk_mapping and disk_mapping['root']['dev'] is not None:
root_device_name = "/dev/" + disk_mapping['root']['dev']
if 'root' in disk_mapping:
root_device_name = block_device.prepend_dev(
disk_mapping['root']['dev'])
else:
root_device_name = None
@@ -2995,12 +3000,7 @@ class LibvirtDriver(driver.ComputeDriver):
for vol in block_device_mapping:
connection_info = vol['connection_info']
disk_dev = vol['mount_device'].rpartition("/")[2]
disk_info = {
'dev': disk_dev,
'bus': blockinfo.get_disk_bus_for_disk_dev(CONF.libvirt_type,
disk_dev),
'type': 'disk',
}
disk_info = blockinfo.get_info_from_bdm(CONF.libvirt_type, vol)
self.volume_driver_method('connect_volume',
connection_info,
disk_info)
@@ -3928,12 +3928,7 @@ class LibvirtDriver(driver.ComputeDriver):
for vol in block_device_mapping:
connection_info = vol['connection_info']
disk_dev = vol['mount_device'].rpartition("/")[2]
disk_info = {
'dev': disk_dev,
'bus': blockinfo.get_disk_bus_for_disk_dev(CONF.libvirt_type,
disk_dev),
'type': 'disk',
}
disk_info = blockinfo.get_info_from_bdm(CONF.libvirt_type, vol)
self.volume_driver_method('connect_volume',
connection_info,
disk_info)
@@ -4522,6 +4517,39 @@ class LibvirtDriver(driver.ComputeDriver):
LOG.info(_('Deletion of %s complete'), target, instance=instance)
return True
@property
def need_legacy_block_device_info(self):
return False
def default_root_device_name(self, instance, image_meta, root_bdm):
disk_bus = blockinfo.get_disk_bus_for_device_type(CONF.libvirt_type,
image_meta,
"disk")
cdrom_bus = blockinfo.get_disk_bus_for_device_type(CONF.libvirt_type,
image_meta,
"cdrom")
root_info = blockinfo.get_root_info(CONF.libvirt_type,
image_meta, root_bdm,
disk_bus, cdrom_bus)
return block_device.prepend_dev(root_info['dev'])
def default_device_names_for_instance(self, instance, root_device_name,
*block_device_lists):
ephemerals, swap, block_device_mapping = block_device_lists[:3]
def _update_func(bdm):
bdm_id = bdm.get('id')
self.virtapi.block_device_mapping_update(
nova_context.get_admin_context(),
bdm_id, bdm)
blockinfo.default_device_names(CONF.libvirt_type,
instance, root_device_name,
_update_func,
ephemerals, swap,
block_device_mapping)
class HostState(object):
"""Manages information about the compute node through libvirt."""

View File

@@ -99,3 +99,10 @@ class VirtAPI(object):
:param legacy: get bdm info in legacy format (or not)
"""
raise NotImplementedError()
def block_device_mapping_update(self, context, bdm_id, bdm_values):
"""Update the databse for the passed block device mapping
:param context: security context
:param bdm: the block device mapping dict
"""
raise NotImplementedError()