diff --git a/nova/tests/virt/libvirt/test_libvirt.py b/nova/tests/virt/libvirt/test_libvirt.py index ae8fce621545..07bd7716e1bc 100644 --- a/nova/tests/virt/libvirt/test_libvirt.py +++ b/nova/tests/virt/libvirt/test_libvirt.py @@ -1863,6 +1863,39 @@ class LibvirtConnTestCase(test.TestCase): {"name": "fake-instance"}, "/dev/sda") + def test_attach_blockio_invalid_hypervisor(self): + self.flags(libvirt_type='fake_type') + self.create_fake_libvirt_mock() + libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup + self.mox.ReplayAll() + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + self.assertRaises(exception.InvalidHypervisorType, + conn.attach_volume, + {"driver_volume_type": "fake", + "data": {"logical_block_size": "4096", + "physical_block_size": "4096"} + }, + {"name": "fake-instance"}, + "/dev/sda") + + def test_attach_blockio_invalid_version(self): + def get_lib_version_stub(): + return (0 * 1000 * 1000) + (9 * 1000) + 8 + self.flags(libvirt_type='qemu') + self.create_fake_libvirt_mock() + libvirt_driver.LibvirtDriver._conn.lookupByName = self.fake_lookup + self.mox.ReplayAll() + conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False) + self.stubs.Set(self.conn, "getLibVersion", get_lib_version_stub) + self.assertRaises(exception.Invalid, + conn.attach_volume, + {"driver_volume_type": "fake", + "data": {"logical_block_size": "4096", + "physical_block_size": "4096"} + }, + {"name": "fake-instance"}, + "/dev/sda") + def test_multi_nic(self): instance_data = dict(self.test_instance) network_info = _fake_network_info(self.stubs, 2) diff --git a/nova/tests/virt/libvirt/test_libvirt_config.py b/nova/tests/virt/libvirt/test_libvirt_config.py index 2d9e52f3c4c6..e4139bf4e549 100644 --- a/nova/tests/virt/libvirt/test_libvirt_config.py +++ b/nova/tests/virt/libvirt/test_libvirt_config.py @@ -546,6 +546,23 @@ class LibvirtConfigGuestDiskTest(LibvirtConfigBaseTest): """) + def test_config_blockio(self): + obj = config.LibvirtConfigGuestDisk() + obj.source_type = "file" + obj.source_path = "/tmp/hello" + obj.target_dev = "/dev/hda" + obj.target_bus = "ide" + obj.logical_block_size = "4096" + obj.physical_block_size = "4096" + + xml = obj.to_xml() + self.assertXmlEqual(""" + + + + + """, xml) + class LibvirtConfigGuestFilesysTest(LibvirtConfigBaseTest): diff --git a/nova/tests/virt/libvirt/test_libvirt_volume.py b/nova/tests/virt/libvirt/test_libvirt_volume.py index a84c41c01b42..48f6285ac261 100644 --- a/nova/tests/virt/libvirt/test_libvirt_volume.py +++ b/nova/tests/virt/libvirt/test_libvirt_volume.py @@ -97,8 +97,31 @@ class LibvirtVolumeTestCase(test.TestCase): } conf = libvirt_driver.connect_volume(connection_info, self.disk_info) tree = conf.format_dom() - self.assertEqual(tree.get('type'), 'block') - self.assertEqual(tree.find('./serial').text, 'fake_serial') + self.assertEqual('block', tree.get('type')) + self.assertEqual('fake_serial', tree.find('./serial').text) + self.assertEqual(None, tree.find('./blockio')) + + def test_libvirt_volume_driver_blockio(self): + libvirt_driver = volume.LibvirtVolumeDriver(self.fake_conn) + connection_info = { + 'driver_volume_type': 'fake', + 'data': { + 'device_path': '/foo', + 'logical_block_size': '4096', + 'physical_block_size': '4096', + }, + 'serial': 'fake_serial', + } + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } + conf = libvirt_driver.connect_volume(connection_info, disk_info) + tree = conf.format_dom() + blockio = tree.find('./blockio') + self.assertEqual('4096', blockio.get('logical_block_size')) + self.assertEqual('4096', blockio.get('physical_block_size')) def iscsi_connection(self, volume, location, iqn): return { diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py index 937e87cf9e2e..505dd4ec99e8 100644 --- a/nova/virt/libvirt/config.py +++ b/nova/virt/libvirt/config.py @@ -476,6 +476,8 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice): self.disk_write_iops_sec = None self.disk_total_bytes_sec = None self.disk_total_iops_sec = None + self.logical_block_size = None + self.physical_block_size = None def format_dom(self): dev = super(LibvirtConfigGuestDisk, self).format_dom() @@ -556,6 +558,20 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice): if len(iotune) > 0: dev.append(iotune) + + # Block size tuning + if (self.logical_block_size is not None or + self.physical_block_size is not None): + + blockio = etree.Element("blockio") + if self.logical_block_size is not None: + blockio.set('logical_block_size', self.logical_block_size) + + if self.physical_block_size is not None: + blockio.set('physical_block_size', self.physical_block_size) + + dev.append(blockio) + return dev diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 77bf3d9532d3..b74a1233d5af 100755 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -291,6 +291,8 @@ MIN_LIBVIRT_CLOSE_CALLBACK_VERSION = (1, 0, 1) REQ_HYPERVISOR_LIVESNAPSHOT = "QEMU" MIN_LIBVIRT_LIVESNAPSHOT_VERSION = (1, 0, 0) MIN_QEMU_LIVESNAPSHOT_VERSION = (1, 3, 0) +# block size tuning requirements +MIN_LIBVIRT_BLOCKIO_VERSION = (0, 10, 2) def libvirt_error_handler(context, err): @@ -1007,6 +1009,27 @@ class LibvirtDriver(driver.ComputeDriver): disk_dev), 'type': 'disk', } + + # Note(cfb): If the volume has a custom block size, check that + # that we are using QEMU/KVM and libvirt >= 0.10.2. The + # prescence of a block size is considered mandatory by + # cinder so we fail if we can't honor the request. + data = {} + if ('data' in connection_info): + data = connection_info['data'] + if ('logical_block_size' in data or 'physical_block_size' in data): + if CONF.libvirt_type != "kvm" and CONF.libvirt_type != "qemu": + msg = _("Volume sets block size, but the current " + "libvirt hypervisor '%s' does not support custom " + "block size") % CONF.libvirt_type + raise exception.InvalidHypervisorType(msg) + + if not self.has_min_version(MIN_LIBVIRT_BLOCKIO_VERSION): + ver = ".".join([str(x) for x in MIN_LIBVIRT_BLOCKIO_VERSION]) + msg = _("Volume sets block size, but libvirt '%s' or later is " + "required.") % ver + raise exception.Invalid(msg) + conf = self.volume_driver_method('connect_volume', connection_info, disk_info) diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py index 26ffaae759d2..737ed949795e 100644 --- a/nova/virt/libvirt/volume.py +++ b/nova/virt/libvirt/volume.py @@ -99,6 +99,15 @@ class LibvirtBaseVolumeDriver(object): conf.target_dev = disk_info['dev'] conf.target_bus = disk_info['bus'] conf.serial = connection_info.get('serial') + + # Support for block size tuning + data = {} + if 'data' in connection_info: + data = connection_info['data'] + if 'logical_block_size' in data: + conf.logical_block_size = data['logical_block_size'] + if 'physical_block_size' in data: + conf.physical_block_size = data['physical_block_size'] return conf def disconnect_volume(self, connection_info, disk_dev):