diff options
author | Jenkins <jenkins@review.openstack.org> | 2013-02-06 18:35:44 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2013-02-06 18:35:44 +0000 |
commit | 72dab7821ed4fba67eb24dca0d8ed60689f6200a (patch) | |
tree | f07cede5c0ddb247280ca45018482773941802e8 | |
parent | d052a664c8d22ba28d8187df5ca375841bd1961a (diff) | |
parent | 7be531fe9462f2b07d4a1abf6687f649d1dfbb89 (diff) | |
download | nova-72dab7821ed4fba67eb24dca0d8ed60689f6200a.tar.gz nova-72dab7821ed4fba67eb24dca0d8ed60689f6200a.tar.xz nova-72dab7821ed4fba67eb24dca0d8ed60689f6200a.zip |
Merge "Refactor code for setting up libvirt disk mappings"
-rw-r--r-- | nova/tests/test_libvirt.py | 178 | ||||
-rw-r--r-- | nova/tests/test_libvirt_blockinfo.py | 396 | ||||
-rw-r--r-- | nova/tests/test_libvirt_volume.py | 98 | ||||
-rw-r--r-- | nova/tests/test_virt_drivers.py | 8 | ||||
-rw-r--r-- | nova/virt/libvirt/blockinfo.py | 387 | ||||
-rw-r--r-- | nova/virt/libvirt/driver.py | 357 | ||||
-rw-r--r-- | nova/virt/libvirt/volume.py | 43 |
7 files changed, 1187 insertions, 280 deletions
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 1bdcb4b82..52e308b85 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -55,6 +55,7 @@ from nova.virt import driver from nova.virt import fake from nova.virt import firewall as base_firewall from nova.virt import images +from nova.virt.libvirt import blockinfo from nova.virt.libvirt import config as vconfig from nova.virt.libvirt import driver as libvirt_driver from nova.virt.libvirt import firewall @@ -356,9 +357,11 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) cfg = conn.get_guest_config(instance_ref, _fake_network_info(self.stubs, 1), - None, None) + None, disk_info) self.assertEquals(cfg.acpi, True) self.assertEquals(cfg.apic, True) self.assertEquals(cfg.memory, 1024 * 1024 * 2) @@ -401,9 +404,11 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) cfg = conn.get_guest_config(instance_ref, _fake_network_info(self.stubs, 2), - None, None) + None, disk_info) self.assertEquals(cfg.acpi, True) self.assertEquals(cfg.memory, 1024 * 1024 * 2) self.assertEquals(cfg.vcpus, 1) @@ -433,14 +438,18 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) - cfg = conn.get_guest_config(instance_ref, [], None, None, - {'root_device_name': 'dev/vdb'}) + block_device_info = {'root_device_name': '/dev/vdb'} + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref, + block_device_info) + cfg = conn.get_guest_config(instance_ref, [], None, disk_info, + None, block_device_info) self.assertEquals(cfg.acpi, False) self.assertEquals(cfg.memory, 1024 * 1024 * 2) self.assertEquals(cfg.vcpus, 1) self.assertEquals(cfg.os_type, "uml") self.assertEquals(cfg.os_boot_dev, None) - self.assertEquals(cfg.os_root, 'dev/vdb') + self.assertEquals(cfg.os_root, '/dev/vdb') self.assertEquals(len(cfg.devices), 3) self.assertEquals(type(cfg.devices[0]), vconfig.LibvirtConfigGuestDisk) @@ -458,7 +467,10 @@ class LibvirtConnTestCase(test.TestCase): {'connection_info': conn_info, 'mount_device': '/dev/vdc'}, {'connection_info': conn_info, 'mount_device': '/dev/vdd'}]} - cfg = conn.get_guest_config(instance_ref, [], None, None, info) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref, info) + cfg = conn.get_guest_config(instance_ref, [], None, disk_info, + None, info) self.assertEquals(type(cfg.devices[2]), vconfig.LibvirtConfigGuestDisk) self.assertEquals(cfg.devices[2].target_dev, 'vdc') @@ -473,12 +485,13 @@ class LibvirtConnTestCase(test.TestCase): # make configdrive.enabled_for() return True instance_ref['config_drive'] = 'ANY_ID' - cfg = conn.get_guest_config(instance_ref, [], None, None) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + cfg = conn.get_guest_config(instance_ref, [], None, disk_info) self.assertEquals(type(cfg.devices[2]), vconfig.LibvirtConfigGuestDisk) - self.assertEquals(cfg.devices[2].target_dev, - conn.default_last_device) + self.assertEquals(cfg.devices[2].target_dev, 'vdz') def test_get_guest_config_with_vnc(self): self.flags(libvirt_type='kvm', @@ -489,7 +502,9 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) - cfg = conn.get_guest_config(instance_ref, [], None, None) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + cfg = conn.get_guest_config(instance_ref, [], None, disk_info) self.assertEquals(len(cfg.devices), 5) self.assertEquals(type(cfg.devices[0]), vconfig.LibvirtConfigGuestDisk) @@ -513,7 +528,9 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) - cfg = conn.get_guest_config(instance_ref, [], None, None) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + cfg = conn.get_guest_config(instance_ref, [], None, disk_info) self.assertEquals(len(cfg.devices), 6) self.assertEquals(type(cfg.devices[0]), vconfig.LibvirtConfigGuestDisk) @@ -542,7 +559,9 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) - cfg = conn.get_guest_config(instance_ref, [], None, None) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + cfg = conn.get_guest_config(instance_ref, [], None, disk_info) self.assertEquals(len(cfg.devices), 6) self.assertEquals(type(cfg.devices[0]), vconfig.LibvirtConfigGuestDisk) @@ -571,7 +590,9 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) - cfg = conn.get_guest_config(instance_ref, [], None, None) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + cfg = conn.get_guest_config(instance_ref, [], None, disk_info) self.assertEquals(len(cfg.devices), 6) self.assertEquals(type(cfg.devices[0]), vconfig.LibvirtConfigGuestDisk) @@ -600,7 +621,9 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) - cfg = conn.get_guest_config(instance_ref, [], None, None) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + cfg = conn.get_guest_config(instance_ref, [], None, disk_info) self.assertEquals(len(cfg.devices), 8) self.assertEquals(type(cfg.devices[0]), vconfig.LibvirtConfigGuestDisk) @@ -629,9 +652,11 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) conf = conn.get_guest_config(instance_ref, - _fake_network_info(self.stubs, 1), - None, None) + _fake_network_info(self.stubs, 1), + None, disk_info) self.assertEquals(conf.cpu, None) def test_get_guest_cpu_config_default_kvm(self): @@ -647,9 +672,11 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) conf = conn.get_guest_config(instance_ref, _fake_network_info(self.stubs, 1), - None, None) + None, disk_info) self.assertEquals(type(conf.cpu), vconfig.LibvirtConfigGuestCPU) self.assertEquals(conf.cpu.mode, "host-model") @@ -662,9 +689,11 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) conf = conn.get_guest_config(instance_ref, - _fake_network_info(self.stubs, 1), - None, None) + _fake_network_info(self.stubs, 1), + None, disk_info) self.assertEquals(conf.cpu, None) def test_get_guest_cpu_config_default_lxc(self): @@ -674,9 +703,11 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, self.test_instance) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) conf = conn.get_guest_config(instance_ref, - _fake_network_info(self.stubs, 1), - None, None) + _fake_network_info(self.stubs, 1), + None, disk_info) self.assertEquals(conf.cpu, None) def test_get_guest_cpu_config_host_passthrough_new(self): @@ -690,9 +721,11 @@ class LibvirtConnTestCase(test.TestCase): instance_ref = db.instance_create(self.context, self.test_instance) self.flags(libvirt_cpu_mode="host-passthrough") + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) conf = conn.get_guest_config(instance_ref, _fake_network_info(self.stubs, 1), - None, None) + None, disk_info) self.assertEquals(type(conf.cpu), vconfig.LibvirtConfigGuestCPU) self.assertEquals(conf.cpu.mode, "host-passthrough") @@ -709,9 +742,11 @@ class LibvirtConnTestCase(test.TestCase): instance_ref = db.instance_create(self.context, self.test_instance) self.flags(libvirt_cpu_mode="host-model") + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) conf = conn.get_guest_config(instance_ref, _fake_network_info(self.stubs, 1), - None, None) + None, disk_info) self.assertEquals(type(conf.cpu), vconfig.LibvirtConfigGuestCPU) self.assertEquals(conf.cpu.mode, "host-model") @@ -729,9 +764,11 @@ class LibvirtConnTestCase(test.TestCase): self.flags(libvirt_cpu_mode="custom") self.flags(libvirt_cpu_model="Penryn") + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) conf = conn.get_guest_config(instance_ref, _fake_network_info(self.stubs, 1), - None, None) + None, disk_info) self.assertEquals(type(conf.cpu), vconfig.LibvirtConfigGuestCPU) self.assertEquals(conf.cpu.mode, "custom") @@ -747,11 +784,14 @@ class LibvirtConnTestCase(test.TestCase): instance_ref = db.instance_create(self.context, self.test_instance) self.flags(libvirt_cpu_mode="host-passthrough") + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) self.assertRaises(exception.NovaException, conn.get_guest_config, instance_ref, _fake_network_info(self.stubs, 1), - None, None) + None, + disk_info) def test_get_guest_cpu_config_host_model_old(self): def get_lib_version_stub(self): @@ -781,9 +821,11 @@ class LibvirtConnTestCase(test.TestCase): instance_ref = db.instance_create(self.context, self.test_instance) self.flags(libvirt_cpu_mode="host-model") + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) conf = conn.get_guest_config(instance_ref, _fake_network_info(self.stubs, 1), - None, None) + None, disk_info) self.assertEquals(type(conf.cpu), vconfig.LibvirtConfigGuestCPU) self.assertEquals(conf.cpu.mode, None) @@ -805,9 +847,11 @@ class LibvirtConnTestCase(test.TestCase): self.flags(libvirt_cpu_mode="custom") self.flags(libvirt_cpu_model="Penryn") + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) conf = conn.get_guest_config(instance_ref, _fake_network_info(self.stubs, 1), - None, None) + None, disk_info) self.assertEquals(type(conf.cpu), vconfig.LibvirtConfigGuestCPU) self.assertEquals(conf.cpu.mode, None) @@ -1572,14 +1616,16 @@ class LibvirtConnTestCase(test.TestCase): conn.attach_volume, {"driver_volume_type": "badtype"}, {"name": "fake-instance"}, - "/dev/fake") + "/dev/sda") def test_multi_nic(self): instance_data = dict(self.test_instance) network_info = _fake_network_info(self.stubs, 2) conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) instance_ref = db.instance_create(self.context, instance_data) - xml = conn.to_xml(instance_ref, network_info, None, False) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + xml = conn.to_xml(instance_ref, network_info, disk_info) tree = etree.fromstring(xml) interfaces = tree.findall("./devices/interface") self.assertEquals(len(interfaces), 2) @@ -1599,7 +1645,9 @@ class LibvirtConnTestCase(test.TestCase): self.assertEquals(conn.uri, 'lxc:///') network_info = _fake_network_info(self.stubs, 1) - xml = conn.to_xml(instance_ref, network_info) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + xml = conn.to_xml(instance_ref, network_info, disk_info) tree = etree.fromstring(xml) check = [ @@ -1640,7 +1688,9 @@ class LibvirtConnTestCase(test.TestCase): conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) network_info = _fake_network_info(self.stubs, 1) - xml = conn.to_xml(instance_ref, network_info) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + xml = conn.to_xml(instance_ref, network_info, disk_info) tree = etree.fromstring(xml) for i, (check, expected_result) in enumerate(checks): @@ -1673,8 +1723,10 @@ class LibvirtConnTestCase(test.TestCase): instance_ref = db.instance_create(user_context, self.test_instance) network_info = _fake_network_info(self.stubs, 1) - xml = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True).to_xml( - instance_ref, network_info, image_meta) + drv = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + xml = drv.to_xml(instance_ref, network_info, disk_info, image_meta) tree = etree.fromstring(xml) disks = tree.findall('./devices/disk/driver') for disk in disks: @@ -1684,8 +1736,10 @@ class LibvirtConnTestCase(test.TestCase): # The O_DIRECT availability is cached on first use in # LibvirtDriver, hence we re-create it here - xml = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True).to_xml( - instance_ref, network_info, image_meta) + drv = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + xml = drv.to_xml(instance_ref, network_info, disk_info, image_meta) tree = etree.fromstring(xml) disks = tree.findall('./devices/disk/driver') for disk in disks: @@ -1697,11 +1751,13 @@ class LibvirtConnTestCase(test.TestCase): instance_ref = db.instance_create(user_context, self.test_instance) network_info = _fake_network_info(self.stubs, 1) - xml = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True).to_xml( - instance_ref, - network_info, - image_meta, - block_device_info=block_device_info) + drv = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref, + block_device_info, + image_meta) + xml = drv.to_xml(instance_ref, network_info, disk_info, image_meta, + block_device_info=block_device_info) tree = etree.fromstring(xml) got_disks = tree.findall('./devices/disk') @@ -1724,8 +1780,10 @@ class LibvirtConnTestCase(test.TestCase): instance_ref = db.instance_create(user_context, self.test_instance) network_info = _fake_network_info(self.stubs, 1) - xml = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True).to_xml( - instance_ref, network_info, image_meta) + drv = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + xml = drv.to_xml(instance_ref, network_info, disk_info, image_meta) tree = etree.fromstring(xml) self.assertEqual(tree.find('./uuid').text, instance_ref['uuid']) @@ -1879,7 +1937,11 @@ class LibvirtConnTestCase(test.TestCase): self.assertEquals(conn.uri, expected_uri) network_info = _fake_network_info(self.stubs, 1) - xml = conn.to_xml(instance_ref, network_info, None, rescue) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref, + rescue=rescue) + xml = conn.to_xml(instance_ref, network_info, disk_info, + rescue=rescue) tree = etree.fromstring(xml) for i, (check, expected_result) in enumerate(checks): self.assertEqual(check(tree), @@ -2216,9 +2278,14 @@ class LibvirtConnTestCase(test.TestCase): ).AndReturn(vol['block_device_mapping']) self.mox.StubOutWithMock(conn, "volume_driver_method") for v in vol['block_device_mapping']: + disk_info = { + 'bus': "scsi", + 'dev': v['mount_device'].rpartition("/")[2], + 'type': "disk" + } conn.volume_driver_method('connect_volume', - v['connection_info'], - v['mount_device'].rpartition("/")[2]) + v['connection_info'], + disk_info) self.mox.StubOutWithMock(conn, 'plug_vifs') conn.plug_vifs(mox.IsA(inst_ref), nw_info) @@ -2244,10 +2311,14 @@ class LibvirtConnTestCase(test.TestCase): # Creating mocks self.mox.StubOutWithMock(conn, "volume_driver_method") for v in vol['block_device_mapping']: + disk_info = { + 'bus': "scsi", + 'dev': v['mount_device'].rpartition("/")[2], + 'type': "disk" + } conn.volume_driver_method('connect_volume', v['connection_info'], - v['mount_device']. - rpartition("/")[2]) + disk_info) self.mox.StubOutWithMock(conn, 'plug_vifs') conn.plug_vifs(mox.IsA(inst_ref), nw_info) self.mox.ReplayAll() @@ -4254,16 +4325,18 @@ class LibvirtDriverTestCase(test.TestCase): def fake_extend(path, size): pass - def fake_to_xml(instance, network_info, image_meta=None, rescue=None, + def fake_to_xml(instance, network_info, disk_info, + image_meta=None, rescue=None, block_device_info=None, write_to_disk=False): return "" def fake_plug_vifs(instance, network_info): pass - def fake_create_image(context, inst, libvirt_xml, suffix='', - disk_images=None, network_info=None, - block_device_info=None): + def fake_create_image(context, inst, libvirt_xml, + disk_mapping, suffix='', + disk_images=None, network_info=None, + block_device_info=None): pass def fake_create_domain(xml, instance=None): @@ -4321,7 +4394,8 @@ class LibvirtDriverTestCase(test.TestCase): def fake_get_info(instance): return {'state': power_state.RUNNING} - def fake_to_xml(instance, network_info, image_meta=None, rescue=None, + def fake_to_xml(instance, network_info, disk_info, + image_meta=None, rescue=None, block_device_info=None): return "" diff --git a/nova/tests/test_libvirt_blockinfo.py b/nova/tests/test_libvirt_blockinfo.py new file mode 100644 index 000000000..23f846324 --- /dev/null +++ b/nova/tests/test_libvirt_blockinfo.py @@ -0,0 +1,396 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# +# Copyright 2010 OpenStack LLC +# Copyright 2012 University Of Minho +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nova import block_device +from nova import context +from nova import db +from nova import test +import nova.tests.image.fake +from nova.virt.libvirt import blockinfo + + +class LibvirtBlockInfoTest(test.TestCase): + + def setUp(self): + super(LibvirtBlockInfoTest, self).setUp() + + self.user_id = 'fake' + self.project_id = 'fake' + self.context = context.get_admin_context() + nova.tests.image.fake.stub_out_image_service(self.stubs) + self.test_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': '5'} # m1.small + + 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}] + block_device_mapping = [{'mount_device': '/dev/sde', + 'device_path': 'fake_device'}, + {'mount_device': '/dev/sdf', + 'device_path': 'fake_device'}] + block_device_info = { + 'root_device_name': '/dev/sda', + 'swap': swap, + 'ephemerals': ephemerals, + 'block_device_mapping': block_device_mapping} + + def _assert_volume_in_mapping(device_name, true_or_false): + self.assertEquals( + block_device.volume_in_mapping(device_name, + block_device_info), + true_or_false) + + _assert_volume_in_mapping('sda', False) + _assert_volume_in_mapping('sdb', True) + _assert_volume_in_mapping('sdc1', True) + _assert_volume_in_mapping('sdd', True) + _assert_volume_in_mapping('sde', True) + _assert_volume_in_mapping('sdf', True) + _assert_volume_in_mapping('sdg', False) + _assert_volume_in_mapping('sdh1', False) + + def test_find_disk_dev(self): + mapping = { + "disk.local": { + 'dev': 'sda', + 'bus': 'scsi', + 'type': 'disk', + }, + "disk.swap": { + 'dev': 'sdc', + 'bus': 'scsi', + 'type': 'disk', + }, + } + + dev = blockinfo.find_disk_dev_for_disk_bus(mapping, 'scsi') + self.assertEqual(dev, 'sdb') + + dev = blockinfo.find_disk_dev_for_disk_bus(mapping, 'scsi', + last_device=True) + self.assertEqual(dev, 'sdz') + + dev = blockinfo.find_disk_dev_for_disk_bus(mapping, 'virtio') + self.assertEqual(dev, 'vda') + + def test_get_next_disk_dev(self): + mapping = {} + mapping['disk.local'] = blockinfo.get_next_disk_info(mapping, + 'virtio') + self.assertEqual(mapping['disk.local'], + {'dev': 'vda', 'bus': 'virtio', 'type': 'disk'}) + + mapping['disk.swap'] = blockinfo.get_next_disk_info(mapping, + 'virtio') + self.assertEqual(mapping['disk.swap'], + {'dev': 'vdb', 'bus': 'virtio', 'type': 'disk'}) + + mapping['disk.config'] = blockinfo.get_next_disk_info(mapping, + 'ide', + 'cdrom', + True) + self.assertEqual(mapping['disk.config'], + {'dev': 'hdd', 'bus': 'ide', 'type': 'cdrom'}) + + def test_get_disk_mapping_simple(self): + # The simplest possible disk mapping setup, all defaults + + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide") + + expect = { + 'disk': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}, + 'disk.local': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'}, + 'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_simple_rootdev(self): + # A simple disk mapping setup, but with custom root device name + + 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/sda' + } + + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide", + block_device_info) + + expect = { + 'disk': {'bus': 'scsi', 'dev': 'sda', 'type': 'disk'}, + 'disk.local': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}, + 'root': {'bus': 'scsi', 'dev': 'sda', 'type': 'disk'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_rescue(self): + # A simple disk mapping setup, but in rescue mode + + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide", + rescue=True) + + expect = { + 'disk.rescue': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}, + 'disk': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'}, + 'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_simple_iso(self): + # A simple disk mapping setup, but with a ISO for root device + + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + image_meta = {'disk_format': 'iso'} + + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide", + None, + image_meta) + + expect = { + 'disk': {'bus': 'ide', 'dev': 'hda', 'type': 'cdrom'}, + 'disk.local': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}, + 'root': {'bus': 'ide', 'dev': 'hda', 'type': 'cdrom'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_simple_swap(self): + # A simple disk mapping setup, but with a swap device added + + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + instance_ref['instance_type']['swap'] = 5 + + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide") + + expect = { + 'disk': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}, + 'disk.local': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'}, + 'disk.swap': {'bus': 'virtio', 'dev': 'vdc', 'type': 'disk'}, + 'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_simple_configdrive(self): + # A simple disk mapping setup, but with configdrive added + + self.flags(force_config_drive=True) + + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide") + + expect = { + 'disk': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}, + 'disk.local': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'}, + 'disk.config': {'bus': 'virtio', 'dev': 'vdz', 'type': 'disk'}, + 'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_ephemeral(self): + # A disk mapping with ephemeral devices + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + instance_ref['instance_type']['swap'] = 5 + + block_device_info = { + 'ephemerals': [ + {'num': 0, 'virtual_name': 'ephemeral0', + 'device_name': '/dev/vdb', 'size': 10}, + {'num': 1, 'virtual_name': 'ephemeral1', + 'device_name': '/dev/vdc', 'size': 10}, + {'num': 2, 'virtual_name': 'ephemeral2', + 'device_name': '/dev/vdd', 'size': 10}, + ] + } + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide", + block_device_info) + + 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.swap': {'bus': 'virtio', 'dev': 'vde', 'type': 'disk'}, + 'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_custom_swap(self): + # A disk mapping with a swap device at position vdb. This + # should cause disk.local to be removed + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + + block_device_info = { + 'swap': {'device_name': '/dev/vdb', + 'swap_size': 10}, + } + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide", + block_device_info) + + expect = { + 'disk': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}, + 'disk.swap': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'}, + 'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_blockdev_root(self): + # A disk mapping with a blockdev replacing the default root + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + + block_device_info = { + 'block_device_mapping': [ + {'connection_info': "fake", + 'mount_device': "/dev/vda", + 'delete_on_termination': True}, + ] + } + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide", + block_device_info) + + expect = { + '/dev/vda': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}, + 'disk.local': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'}, + 'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_blockdev_eph(self): + # A disk mapping with a blockdev replacing the ephemeral device + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + + block_device_info = { + 'block_device_mapping': [ + {'connection_info': "fake", + 'mount_device': "/dev/vdb", + 'delete_on_termination': True}, + ] + } + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide", + block_device_info) + + expect = { + 'disk': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'}, + '/dev/vdb': {'bus': 'virtio', 'dev': 'vdb', 'type': 'disk'}, + 'root': {'bus': 'virtio', 'dev': 'vda', 'type': 'disk'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_blockdev_many(self): + # A disk mapping with a blockdev replacing all devices + user_context = context.RequestContext(self.user_id, self.project_id) + instance_ref = db.instance_create(user_context, self.test_instance) + + block_device_info = { + 'block_device_mapping': [ + {'connection_info': "fake", + 'mount_device': "/dev/vda", + 'delete_on_termination': True}, + {'connection_info': "fake", + 'mount_device': "/dev/vdb", + 'delete_on_termination': True}, + {'connection_info': "fake", + 'mount_device': "/dev/vdc", + 'delete_on_termination': True}, + ] + } + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide", + block_device_info) + + expect = { + '/dev/vda': {'bus': 'virtio', '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'} + } + self.assertEqual(mapping, expect) + + def test_get_disk_mapping_complex(self): + # The strangest possible disk mapping setup + 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/vdf', + 'swap': {'device_name': '/dev/vdy', + 'swap_size': 10}, + 'ephemerals': [ + {'num': 0, 'virtual_name': 'ephemeral0', + 'device_name': '/dev/vdb', 'size': 10}, + {'num': 1, 'virtual_name': 'ephemeral1', + 'device_name': '/dev/vdc', 'size': 10}, + ], + 'block_device_mapping': [ + {'connection_info': "fake", + 'mount_device': "/dev/vda", + 'delete_on_termination': True}, + ] + } + mapping = blockinfo.get_disk_mapping("kvm", instance_ref, + "virtio", "ide", + block_device_info) + + 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.swap': {'bus': 'virtio', 'dev': 'vdy', 'type': 'disk'}, + 'root': {'bus': 'virtio', 'dev': 'vdf', 'type': 'disk'} + } + self.assertEqual(mapping, expect) diff --git a/nova/tests/test_libvirt_volume.py b/nova/tests/test_libvirt_volume.py index 6cc8ee2f6..b9f95735f 100644 --- a/nova/tests/test_libvirt_volume.py +++ b/nova/tests/test_libvirt_volume.py @@ -66,8 +66,12 @@ class LibvirtVolumeTestCase(test.TestCase): }, 'serial': 'fake_serial', } - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() self.assertEqual(tree.get('type'), 'block') self.assertEqual(tree.find('./serial').text, 'fake_serial') @@ -92,13 +96,17 @@ class LibvirtVolumeTestCase(test.TestCase): iqn = 'iqn.2010-10.org.openstack:%s' % name vol = {'id': 1, 'name': name} connection_info = self.iscsi_connection(vol, location, iqn) - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() dev_str = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-1' % (location, iqn) self.assertEqual(tree.get('type'), 'block') self.assertEqual(tree.find('./source').get('dev'), dev_str) - libvirt_driver.disconnect_volume(connection_info, mount_device) + libvirt_driver.disconnect_volume(connection_info, "vde") expected_commands = [('iscsiadm', '-m', 'node', '-T', iqn, '-p', location), ('iscsiadm', '-m', 'node', '-T', iqn, @@ -126,13 +134,17 @@ class LibvirtVolumeTestCase(test.TestCase): self.stubs.Set(self.fake_conn, 'get_all_block_devices', lambda: devs) vol = {'id': 1, 'name': name} connection_info = self.iscsi_connection(vol, location, iqn) - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() dev_str = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-1' % (location, iqn) self.assertEqual(tree.get('type'), 'block') self.assertEqual(tree.find('./source').get('dev'), dev_str) - libvirt_driver.disconnect_volume(connection_info, mount_device) + libvirt_driver.disconnect_volume(connection_info, "vde") expected_commands = [('iscsiadm', '-m', 'node', '-T', iqn, '-p', location), ('iscsiadm', '-m', 'node', '-T', iqn, @@ -155,13 +167,17 @@ class LibvirtVolumeTestCase(test.TestCase): name = 'volume-00000001' vol = {'id': 1, 'name': name} connection_info = self.sheepdog_connection(vol) - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() self.assertEqual(tree.get('type'), 'network') self.assertEqual(tree.find('./source').get('protocol'), 'sheepdog') self.assertEqual(tree.find('./source').get('name'), name) - libvirt_driver.disconnect_volume(connection_info, mount_device) + libvirt_driver.disconnect_volume(connection_info, "vde") def rbd_connection(self, volume): return { @@ -180,15 +196,19 @@ class LibvirtVolumeTestCase(test.TestCase): name = 'volume-00000001' vol = {'id': 1, 'name': name} connection_info = self.rbd_connection(vol) - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() self.assertEqual(tree.get('type'), 'network') self.assertEqual(tree.find('./source').get('protocol'), 'rbd') rbd_name = '%s/%s' % ('rbd', name) self.assertEqual(tree.find('./source').get('name'), rbd_name) self.assertEqual(tree.find('./source/auth'), None) - libvirt_driver.disconnect_volume(connection_info, mount_device) + libvirt_driver.disconnect_volume(connection_info, "vde") def test_libvirt_rbd_driver_auth_enabled(self): libvirt_driver = volume.LibvirtNetVolumeDriver(self.fake_conn) @@ -202,9 +222,13 @@ class LibvirtVolumeTestCase(test.TestCase): connection_info['data']['auth_username'] = user connection_info['data']['secret_type'] = secret_type connection_info['data']['secret_uuid'] = uuid + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() self.assertEqual(tree.get('type'), 'network') self.assertEqual(tree.find('./source').get('protocol'), 'rbd') @@ -213,7 +237,7 @@ class LibvirtVolumeTestCase(test.TestCase): self.assertEqual(tree.find('./auth').get('username'), user) self.assertEqual(tree.find('./auth/secret').get('type'), secret_type) self.assertEqual(tree.find('./auth/secret').get('uuid'), uuid) - libvirt_driver.disconnect_volume(connection_info, mount_device) + libvirt_driver.disconnect_volume(connection_info, "vde") def test_libvirt_rbd_driver_auth_enabled_flags_override(self): libvirt_driver = volume.LibvirtNetVolumeDriver(self.fake_conn) @@ -232,9 +256,13 @@ class LibvirtVolumeTestCase(test.TestCase): flags_user = 'bar' self.flags(rbd_user=flags_user, rbd_secret_uuid=flags_uuid) + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() self.assertEqual(tree.get('type'), 'network') self.assertEqual(tree.find('./source').get('protocol'), 'rbd') @@ -243,7 +271,7 @@ class LibvirtVolumeTestCase(test.TestCase): self.assertEqual(tree.find('./auth').get('username'), flags_user) self.assertEqual(tree.find('./auth/secret').get('type'), secret_type) self.assertEqual(tree.find('./auth/secret').get('uuid'), flags_uuid) - libvirt_driver.disconnect_volume(connection_info, mount_device) + libvirt_driver.disconnect_volume(connection_info, "vde") def test_libvirt_rbd_driver_auth_disabled(self): libvirt_driver = volume.LibvirtNetVolumeDriver(self.fake_conn) @@ -257,16 +285,20 @@ class LibvirtVolumeTestCase(test.TestCase): connection_info['data']['auth_username'] = user connection_info['data']['secret_type'] = secret_type connection_info['data']['secret_uuid'] = uuid + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() self.assertEqual(tree.get('type'), 'network') self.assertEqual(tree.find('./source').get('protocol'), 'rbd') rbd_name = '%s/%s' % ('rbd', name) self.assertEqual(tree.find('./source').get('name'), rbd_name) self.assertEqual(tree.find('./auth'), None) - libvirt_driver.disconnect_volume(connection_info, mount_device) + libvirt_driver.disconnect_volume(connection_info, "vde") def test_libvirt_rbd_driver_auth_disabled_flags_override(self): libvirt_driver = volume.LibvirtNetVolumeDriver(self.fake_conn) @@ -287,9 +319,13 @@ class LibvirtVolumeTestCase(test.TestCase): flags_user = 'bar' self.flags(rbd_user=flags_user, rbd_secret_uuid=flags_uuid) + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() self.assertEqual(tree.get('type'), 'network') self.assertEqual(tree.find('./source').get('protocol'), 'rbd') @@ -298,7 +334,7 @@ class LibvirtVolumeTestCase(test.TestCase): self.assertEqual(tree.find('./auth').get('username'), flags_user) self.assertEqual(tree.find('./auth/secret').get('type'), secret_type) self.assertEqual(tree.find('./auth/secret').get('uuid'), flags_uuid) - libvirt_driver.disconnect_volume(connection_info, mount_device) + libvirt_driver.disconnect_volume(connection_info, "vde") def test_libvirt_nfs_driver(self): # NOTE(vish) exists is to make driver assume connecting worked @@ -313,12 +349,16 @@ class LibvirtVolumeTestCase(test.TestCase): file_path = os.path.join(export_mnt_base, name) connection_info = {'data': {'export': export_string, 'name': name}} - mount_device = "vde" - conf = libvirt_driver.connect_volume(connection_info, mount_device) + disk_info = { + "bus": "virtio", + "dev": "vde", + "type": "disk", + } + conf = libvirt_driver.connect_volume(connection_info, disk_info) tree = conf.format_dom() self.assertEqual(tree.get('type'), 'file') self.assertEqual(tree.find('./source').get('file'), file_path) - libvirt_driver.disconnect_volume(connection_info, mount_device) + libvirt_driver.disconnect_volume(connection_info, "vde") expected_commands = [ ('stat', export_mnt_base), diff --git a/nova/tests/test_virt_drivers.py b/nova/tests/test_virt_drivers.py index e8e7c329a..29c7f9959 100644 --- a/nova/tests/test_virt_drivers.py +++ b/nova/tests/test_virt_drivers.py @@ -378,10 +378,10 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): instance_ref, network_info = self._get_running_instance() self.connection.attach_volume({'driver_volume_type': 'fake'}, instance_ref, - '/mnt/nova/something') + '/dev/sda') self.connection.detach_volume({'driver_volume_type': 'fake'}, instance_ref, - '/mnt/nova/something') + '/dev/sda') @catch_notimplementederror def test_attach_detach_different_power_states(self): @@ -389,11 +389,11 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase): self.connection.power_off(instance_ref) self.connection.attach_volume({'driver_volume_type': 'fake'}, instance_ref, - '/mnt/nova/something') + '/dev/sda') self.connection.power_on(instance_ref) self.connection.detach_volume({'driver_volume_type': 'fake'}, instance_ref, - '/mnt/nova/something') + '/dev/sda') @catch_notimplementederror def test_get_info(self): diff --git a/nova/virt/libvirt/blockinfo.py b/nova/virt/libvirt/blockinfo.py new file mode 100644 index 000000000..844ae4a1b --- /dev/null +++ b/nova/virt/libvirt/blockinfo.py @@ -0,0 +1,387 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (C) 2012-2013 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +""" +Handling of block device information and mapping. + +This module contains helper methods for intepreting the block +device information and determining the suitable mapping to +guest devices and libvirt XML. + +Throughout these methods there are a number of standard +variables / types used + + * 'mapping': a dict contains the storage device mapping. + + For the default disk types it will contain the following + keys & values: + + 'disk' -> disk_info + 'disk.rescue' -> disk_info + 'disk.local' -> disk_info + 'disk.swap' -> disk_info + 'disk.config' -> disk_info + + If any of the default disks are overriden by the block + device info mappings, the hash value will be None + + For any ephemeral device there will also be a dict entry + + 'disk.eph$NUM' -> disk_info + + For any volume device there will also be a dict entry: + + $path -> disk_info + + Finally a special key will refer to the root device: + + 'root' -> disk_info + + + * 'disk_info': a tuple specifying disk configuration + + It contains the following 3 fields + + (disk bus, disk dev, device type) + + * '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' + +""" + +from nova import block_device +from nova import exception +from nova.openstack.common import log as logging +from nova.virt import configdrive +from nova.virt import driver + + +LOG = logging.getLogger(__name__) + + +def has_disk_dev(mapping, disk_dev): + """Determine if a disk device name has already been used. + + Looks at all the keys in mapping to see if any + corresponding disk_info tuple has a device name + matching disk_dev + + Returns True if the disk_dev is in use.""" + + for disk in mapping: + info = mapping[disk] + if info['dev'] == disk_dev: + return True + return False + + +def get_dev_prefix_for_disk_bus(disk_bus): + """Determine the dev prefix for a disk bus. + + Determine the dev prefix to be combined + with a disk number to fix a disk_dev. + eg 'hd' for 'ide' bus can be used to + form a disk dev 'hda' + + Returns the dev prefix or raises an + exception if the disk bus is unknown.""" + + if disk_bus == "ide": + return "hd" + elif disk_bus == "virtio": + return "vd" + elif disk_bus == "xen": + # Two possible mappings for Xen, xvda or sda + # which are interchangable, so we pick sda + return "sd" + elif disk_bus == "scsi": + return "sd" + elif disk_bus == "usb": + return "sd" + elif disk_bus == "uml": + return "ubd" + else: + raise exception.NovaException( + _("Unable to determine disk prefix for %s") % + disk_bus) + + +def get_dev_count_for_disk_bus(disk_bus): + """Determine the number disks supported. + + Determine how many disks can be supported in + a single VM for a particular disk bus. + + Returns the number of disks supported.""" + + if disk_bus == "ide": + return 4 + else: + return 26 + + +def find_disk_dev_for_disk_bus(mapping, bus, last_device=False): + """Identify a free disk dev name for a bus. + + Determines the possible disk dev names for + the bus, and then checks them in order until + it identifies one that is not yet used in the + disk mapping. If 'last_device' is set, it will + only consider the last available disk dev name. + + Returns the chosen disk_dev name, or raises an + exception if none is available. + """ + + dev_prefix = get_dev_prefix_for_disk_bus(bus) + max_dev = get_dev_count_for_disk_bus(bus) + if last_device: + devs = [max_dev - 1] + else: + devs = range(max_dev) + + for idx in devs: + disk_dev = dev_prefix + chr(ord('a') + idx) + if not has_disk_dev(mapping, disk_dev): + return disk_dev + + raise exception.NovaException( + _("No free disk device names for prefix '%s'"), + dev_prefix) + + +def get_disk_bus_for_device_type(virt_type, device_type="disk"): + """Determine the best disk bus to use for a device type. + + Considering the currently configured virtualization + type, return the optimal disk_bus to use for a given + device type. For example, for a disk on KVM it will + return 'virtio', while for a CDROM it will return 'ide' + + Returns the disk_bus, or returns None if the device + type is not supported for this virtualization""" + + if virt_type == "uml": + if device_type == "disk": + return "uml" + elif virt_type == "xen": + if device_type == "cdrom": + return "ide" + elif device_type == "disk": + return "xen" + elif virt_type in ("qemu", "kvm"): + if device_type == "cdrom": + return "ide" + elif device_type == "disk": + return "virtio" + + return None + + +def get_disk_bus_for_disk_dev(virt_type, disk_dev): + """Determine the disk bus for a disk dev. + + Given a disk devi like 'hda', 'sdf', 'xvdb', etc + guess what the most appropriate disk bus is for + the currently configured virtualization technology + + Returns the disk bus, or raises an Exception if + the disk dev prefix is unknown.""" + + if disk_dev[:2] == 'hd': + return "ide" + elif disk_dev[:2] == 'sd': + # Reverse mapping 'sd' is not reliable + # there are many possible mappings. So + # this picks the most likely mappings + if virt_type == "xen": + return "xen" + else: + return "scsi" + elif disk_dev[:2] == 'vd': + return "virtio" + elif disk_dev[:3] == 'xvd': + return "xen" + elif disk_dev[:3] == 'ubd': + return "uml" + else: + raise exception.NovaException( + _("Unable to determine disk bus for '%s'") % + disk_dev[:1]) + + +def get_next_disk_info(mapping, disk_bus, + device_type='disk', + last_device=False): + """Determine the disk info for the next device on disk_bus. + + Considering the disks already listed in the disk mapping, + determine the next available disk dev that can be assigned + for the disk bus. + + Returns the disk_info for the next available disk.""" + + disk_dev = find_disk_dev_for_disk_bus(mapping, + disk_bus, + last_device) + return {'bus': disk_bus, + 'dev': disk_dev, + 'type': device_type} + + +def get_eph_disk(ephemeral): + return 'disk.eph' + str(ephemeral['num']) + + +def get_disk_mapping(virt_type, instance, + disk_bus, cdrom_bus, + block_device_info=None, + image_meta=None, rescue=False): + """Determine how to map default disks to the virtual machine. + + This is about figuring out whether the default 'disk', + 'disk.local', 'disk.swap' and 'disk.config' images have + been overriden by the block device mapping. + + Returns the guest disk mapping for the devices.""" + + inst_type = instance['instance_type'] + + mapping = {} + + if virt_type == "lxc": + return mapping + + if rescue: + rescue_info = get_next_disk_info(mapping, + disk_bus) + mapping['disk.rescue'] = rescue_info + mapping['root'] = rescue_info + + os_info = get_next_disk_info(mapping, + disk_bus) + mapping['disk'] = os_info + + 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' + + 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): + 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 + + 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'} + + 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'} + elif inst_type['swap'] > 0: + swap_info = get_next_disk_info(mapping, + disk_bus) + if not block_device.volume_in_mapping(swap_info['dev'], + block_device_info): + mapping['disk.swap'] = swap_info + + block_device_mapping = driver.block_device_info_get_mapping( + 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'} + + if configdrive.enabled_for(instance): + config_info = get_next_disk_info(mapping, + disk_bus, + last_device=True) + mapping['disk.config'] = config_info + + return mapping + + +def get_disk_info(virt_type, instance, block_device_info=None, + image_meta=None, rescue=False): + """Determine guest disk mapping info. + + This is a wrapper around get_disk_mapping, which + also returns the chosen disk_bus and cdrom_bus. + The returned data is in a dict + + - disk_bus: the bus for harddisks + - cdrom_bus: the bus for CDROMs + - mapping: the disk mapping + + Returns the disk mapping disk.""" + + disk_bus = get_disk_bus_for_device_type(virt_type, "disk") + cdrom_bus = get_disk_bus_for_device_type(virt_type, "cdrom") + mapping = get_disk_mapping(virt_type, instance, + disk_bus, cdrom_bus, + block_device_info, + image_meta, rescue) + + return {'disk_bus': disk_bus, + 'cdrom_bus': cdrom_bus, + 'mapping': mapping} diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index fef44d6b6..901616c89 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -76,6 +76,7 @@ from nova.virt import configdrive from nova.virt.disk import api as disk from nova.virt import driver from nova.virt import firewall +from nova.virt.libvirt import blockinfo from nova.virt.libvirt import config as vconfig from nova.virt.libvirt import firewall as libvirt_firewall from nova.virt.libvirt import imagebackend @@ -261,10 +262,6 @@ MIN_LIBVIRT_LIVESNAPSHOT_VERSION = (1, 0, 0) MIN_QEMU_LIVESNAPSHOT_VERSION = (1, 3, 0) -def _get_eph_disk(ephemeral): - return 'disk.eph' + str(ephemeral['num']) - - class LibvirtDriver(driver.ComputeDriver): capabilities = { @@ -295,16 +292,6 @@ class LibvirtDriver(driver.ComputeDriver): self._host_state = None - disk_prefix_map = {"lxc": "", "uml": "ubd", "xen": "sd"} - if CONF.libvirt_disk_prefix: - self._disk_prefix = CONF.libvirt_disk_prefix - else: - self._disk_prefix = disk_prefix_map.get(CONF.libvirt_type, 'vd') - self.default_root_device = self._disk_prefix + 'a' - self.default_second_device = self._disk_prefix + 'b' - self.default_third_device = self._disk_prefix + 'c' - self.default_last_device = self._disk_prefix + 'z' - self._disk_cachemode = None self.image_cache_manager = imagecache.ImageCacheManager() self.image_backend = imagebackend.Backend(CONF.use_cow_images) @@ -599,10 +586,10 @@ class LibvirtDriver(driver.ComputeDriver): block_device_info) for vol in block_device_mapping: connection_info = vol['connection_info'] - mount_device = vol['mount_device'].rpartition("/")[2] + disk_dev = vol['mount_device'].rpartition("/")[2] self.volume_driver_method('disconnect_volume', connection_info, - mount_device) + disk_dev) if destroy_disks: target = libvirt_utils.get_instance_path(instance) @@ -682,10 +669,16 @@ class LibvirtDriver(driver.ComputeDriver): def attach_volume(self, connection_info, instance, mountpoint): instance_name = instance['name'] virt_dom = self._lookup_by_name(instance_name) - mount_device = mountpoint.rpartition("/")[2] + disk_dev = mountpoint.rpartition("/")[2] + disk_info = { + 'dev': disk_dev, + 'bus': blockinfo.get_disk_bus_for_disk_dev(CONF.libvirt_type, + disk_dev), + 'type': 'disk', + } conf = self.volume_driver_method('connect_volume', connection_info, - mount_device) + disk_info) try: # NOTE(vish): We can always affect config because our @@ -701,14 +694,14 @@ class LibvirtDriver(driver.ComputeDriver): errcode = ex.get_error_code() if errcode == libvirt.VIR_ERR_OPERATION_FAILED: self.volume_driver_method('disconnect_volume', - connection_info, - mount_device) - raise exception.DeviceIsBusy(device=mount_device) + connection_info, + disk_dev) + raise exception.DeviceIsBusy(device=disk_dev) with excutils.save_and_reraise_exception(): self.volume_driver_method('disconnect_volume', - connection_info, - mount_device) + connection_info, + disk_dev) @staticmethod def _get_disk_xml(xml, device): @@ -730,18 +723,21 @@ class LibvirtDriver(driver.ComputeDriver): virt_dom = self._lookup_by_name(instance['name']) xml = virt_dom.XMLDesc(0) except exception.InstanceNotFound: - xml = self.to_xml(instance, network_info, + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance, + block_device_info) + xml = self.to_xml(instance, network_info, disk_info, block_device_info=block_device_info) return xml def detach_volume(self, connection_info, instance, mountpoint): instance_name = instance['name'] - mount_device = mountpoint.rpartition("/")[2] + disk_dev = mountpoint.rpartition("/")[2] try: virt_dom = self._lookup_by_name(instance_name) - xml = self._get_disk_xml(virt_dom.XMLDesc(0), mount_device) + xml = self._get_disk_xml(virt_dom.XMLDesc(0), disk_dev) if not xml: - raise exception.DiskNotFound(location=mount_device) + raise exception.DiskNotFound(location=disk_dev) else: # NOTE(vish): We can always affect config because our # domains are persistent, but we should only @@ -764,7 +760,7 @@ class LibvirtDriver(driver.ComputeDriver): self.volume_driver_method('disconnect_volume', connection_info, - mount_device) + disk_dev) def snapshot(self, context, instance, image_href, update_task_state): """Create snapshot from a running VM instance. @@ -1031,7 +1027,10 @@ class LibvirtDriver(driver.ComputeDriver): """ self._destroy(instance) - xml = self.to_xml(instance, network_info, + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance, + block_device_info) + xml = self.to_xml(instance, network_info, disk_info, block_device_info=block_device_info, write_to_disk=True) self._create_domain_and_network(xml, instance, network_info, @@ -1124,9 +1123,16 @@ class LibvirtDriver(driver.ComputeDriver): 'kernel_id': CONF.rescue_kernel_id or instance['kernel_id'], 'ramdisk_id': CONF.rescue_ramdisk_id or instance['ramdisk_id'], } - xml = self.to_xml(instance, network_info, image_meta, - rescue=rescue_images) - self._create_image(context, instance, xml, '.rescue', rescue_images, + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance, + None, + image_meta, + rescue=True) + xml = self.to_xml(instance, network_info, disk_info, + image_meta, rescue=rescue_images) + self._create_image(context, instance, xml, + disk_info['mapping'], + '.rescue', rescue_images, network_info=network_info, admin_pass=rescue_password) self._destroy(instance) @@ -1162,14 +1168,20 @@ class LibvirtDriver(driver.ComputeDriver): # for xenapi(tr3buchet) def spawn(self, context, instance, image_meta, injected_files, admin_password, network_info=None, block_device_info=None): - xml = self.to_xml(instance, network_info, image_meta, + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance, + block_device_info, + image_meta) + xml = self.to_xml(instance, network_info, + disk_info, image_meta, block_device_info=block_device_info) if image_meta: self._create_image(context, instance, xml, - network_info=network_info, - block_device_info=block_device_info, - files=injected_files, - admin_pass=admin_password) + disk_info['mapping'], + network_info=network_info, + block_device_info=block_device_info, + files=injected_files, + admin_pass=admin_password) self._create_domain_and_network(xml, instance, network_info, block_device_info) LOG.debug(_("Instance is running"), instance=instance) @@ -1372,7 +1384,8 @@ class LibvirtDriver(driver.ComputeDriver): if os.path.exists(console_log): libvirt_utils.chown(console_log, os.getuid()) - def _create_image(self, context, instance, libvirt_xml, suffix='', + def _create_image(self, context, instance, libvirt_xml, + disk_mapping, suffix='', disk_images=None, network_info=None, block_device_info=None, files=None, admin_pass=None): if not suffix: @@ -1432,8 +1445,7 @@ class LibvirtDriver(driver.ComputeDriver): if size == 0 or suffix == '.rescue': size = None - if not block_device.volume_in_mapping( - self.default_root_device, block_device_info): + if 'disk' in disk_mapping: image('disk').cache(fetch_func=libvirt_utils.fetch_image, context=context, filename=root_fname, @@ -1448,9 +1460,7 @@ class LibvirtDriver(driver.ComputeDriver): os_type_with_default = 'default' ephemeral_gb = instance['ephemeral_gb'] - if ephemeral_gb and not block_device.volume_in_mapping( - self.default_second_device, block_device_info): - swap_device = self.default_third_device + if 'disk.local' in disk_mapping: fn = functools.partial(self._create_ephemeral, fs_label='ephemeral0', os_type=instance["os_type"]) @@ -1460,8 +1470,6 @@ class LibvirtDriver(driver.ComputeDriver): filename=fname, size=size, ephemeral_size=ephemeral_gb) - else: - swap_device = self.default_second_device for eph in driver.block_device_info_get_ephemerals(block_device_info): fn = functools.partial(self._create_ephemeral, @@ -1469,27 +1477,29 @@ class LibvirtDriver(driver.ComputeDriver): os_type=instance["os_type"]) size = eph['size'] * 1024 * 1024 * 1024 fname = "ephemeral_%s_%s" % (eph['size'], os_type_with_default) - image(_get_eph_disk(eph)).cache(fetch_func=fn, - filename=fname, - size=size, - ephemeral_size=eph['size']) - - swap_mb = 0 - - swap = driver.block_device_info_get_swap(block_device_info) - if driver.swap_is_usable(swap): - swap_mb = swap['swap_size'] - elif (inst_type['swap'] > 0 and - not block_device.volume_in_mapping( - swap_device, block_device_info)): - swap_mb = inst_type['swap'] - - if swap_mb > 0: - size = swap_mb * 1024 * 1024 - image('disk.swap').cache(fetch_func=self._create_swap, - filename="swap_%s" % swap_mb, - size=size, - swap_mb=swap_mb) + image(blockinfo.get_eph_disk(eph)).cache( + fetch_func=fn, + filename=fname, + size=size, + ephemeral_size=eph['size']) + + if 'disk.swap' in disk_mapping: + swap_mb = 0 + + swap = driver.block_device_info_get_swap(block_device_info) + if driver.swap_is_usable(swap): + swap_mb = swap['swap_size'] + elif (inst_type['swap'] > 0 and + not block_device.volume_in_mapping( + swap['dev'], block_device_info)): + swap_mb = inst_type['swap'] + + if swap_mb > 0: + size = swap_mb * 1024 * 1024 + image('disk.swap').cache(fetch_func=self._create_swap, + filename="swap_%s" % swap_mb, + size=size, + swap_mb=swap_mb) # target partition for file injection target_partition = None @@ -1644,11 +1654,23 @@ class LibvirtDriver(driver.ComputeDriver): return cpu + def get_guest_disk_config(self, instance, name, disk_mapping, + image_type=None): + image = self.image_backend.image(instance, + name, + image_type) + disk_info = disk_mapping[name] + return image.libvirt_info(disk_info['bus'], + disk_info['dev'], + disk_info['type'], + self.disk_cachemode) + def get_guest_storage_config(self, instance, image_meta, + disk_info, rescue, block_device_info, - inst_type, - root_device_name, root_device): + inst_type): devices = [] + disk_mapping = disk_info['mapping'] block_device_mapping = driver.block_device_info_get_mapping( block_device_info) @@ -1660,112 +1682,64 @@ class LibvirtDriver(driver.ComputeDriver): libvirt_utils.get_instance_path(instance), 'rootfs') devices.append(fs) else: - if image_meta and image_meta.get('disk_format') == 'iso': - root_device_type = 'cdrom' - root_device = 'hda' - else: - root_device_type = 'disk' - - if CONF.libvirt_type == "uml": - default_disk_bus = "uml" - elif CONF.libvirt_type == "xen": - default_disk_bus = "xen" - else: - default_disk_bus = "virtio" - - def disk_info(name, disk_dev, disk_bus=default_disk_bus, - device_type="disk"): - image = self.image_backend.image(instance, name) - return image.libvirt_info(disk_bus, - disk_dev, - device_type, - self.disk_cachemode) if rescue: - diskrescue = disk_info('disk.rescue', - self.default_root_device, - device_type=root_device_type) + diskrescue = self.get_guest_disk_config(instance, + 'disk.rescue', + disk_mapping) devices.append(diskrescue) - diskos = disk_info('disk', - self.default_second_device) + diskos = self.get_guest_disk_config(instance, + 'disk', + disk_mapping) devices.append(diskos) else: - ebs_root = block_device.volume_in_mapping( - self.default_root_device, block_device_info) - - if not ebs_root: - if root_device_type == "cdrom": - bus = "ide" - else: - bus = default_disk_bus - diskos = disk_info('disk', - root_device, - bus, - root_device_type) + if 'disk' in disk_mapping: + diskos = self.get_guest_disk_config(instance, + 'disk', + disk_mapping) devices.append(diskos) - ephemeral_device = None - if not (block_device.volume_in_mapping( - self.default_second_device, 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 = self.default_second_device - - if ephemeral_device is not None: - disklocal = disk_info('disk.local', ephemeral_device) + if 'disk.local' in disk_mapping: + disklocal = self.get_guest_disk_config(instance, + 'disk.local', + disk_mapping) devices.append(disklocal) - - if ephemeral_device is not None: - swap_device = self.default_third_device self.virtapi.instance_update( nova_context.get_admin_context(), instance['uuid'], {'default_ephemeral_device': - '/dev/' + self.default_second_device}) - else: - swap_device = self.default_second_device + '/dev/' + disklocal.target_dev}) for eph in driver.block_device_info_get_ephemerals( block_device_info): - diskeph = disk_info(_get_eph_disk(eph), - block_device.strip_dev( - eph['device_name'])) + diskeph = self.get_guest_disk_config( + instance, + blockinfo.get_eph_disk(eph), + disk_mapping) devices.append(diskeph) - swap = driver.block_device_info_get_swap(block_device_info) - if driver.swap_is_usable(swap): - diskswap = disk_info('disk.swap', - block_device.strip_dev( - swap['device_name'])) - devices.append(diskswap) - elif (inst_type['swap'] > 0 and - not block_device.volume_in_mapping( - swap_device, block_device_info)): - diskswap = disk_info('disk.swap', swap_device) + if 'disk.swap' in disk_mapping: + diskswap = self.get_guest_disk_config(instance, + 'disk.swap', + disk_mapping) devices.append(diskswap) self.virtapi.instance_update( nova_context.get_admin_context(), instance['uuid'], - {'default_swap_device': '/dev/' + swap_device}) + {'default_swap_device': '/dev/' + diskswap.target_dev}) for vol in block_device_mapping: connection_info = vol['connection_info'] - mount_device = vol['mount_device'].rpartition("/")[2] + info = disk_mapping[vol['mount_device']] cfg = self.volume_driver_method('connect_volume', connection_info, - mount_device) + info) devices.append(cfg) - if configdrive.enabled_for(instance): - diskconfig = vconfig.LibvirtConfigGuestDisk() - diskconfig.source_type = "file" - diskconfig.driver_format = "raw" - diskconfig.driver_cache = self.disk_cachemode - diskconfig.source_path = os.path.join( - libvirt_utils.get_instance_path(instance), "disk.config") - diskconfig.target_dev = self.default_last_device - diskconfig.target_bus = default_disk_bus + if 'disk.config' in disk_mapping: + diskconfig = self.get_guest_disk_config(instance, + 'disk.config', + disk_mapping, + 'raw') devices.append(diskconfig) return devices @@ -1782,8 +1756,8 @@ class LibvirtDriver(driver.ComputeDriver): return sysinfo - def get_guest_config(self, instance, network_info, image_meta, rescue=None, - block_device_info=None): + def get_guest_config(self, instance, network_info, image_meta, + disk_info, rescue=None, block_device_info=None): """Get config data for parameters. :param rescue: optional dictionary that should contain the key @@ -1792,6 +1766,7 @@ class LibvirtDriver(driver.ComputeDriver): """ inst_type = instance['instance_type'] inst_path = libvirt_utils.get_instance_path(instance) + disk_mapping = disk_info['mapping'] guest = vconfig.LibvirtConfigGuest() guest.virt_type = CONF.libvirt_type @@ -1802,16 +1777,17 @@ class LibvirtDriver(driver.ComputeDriver): guest.cpu = self.get_guest_cpu_config() - root_device_name = driver.block_device_info_get_root(block_device_info) - if root_device_name: - root_device = block_device.strip_dev(root_device_name) + if 'root' in disk_mapping: + root_device_name = "/dev/" + disk_mapping['root']['dev'] else: + root_device_name = None + + if root_device_name: # NOTE(yamahata): # for nova.api.ec2.cloud.CloudController.get_metadata() - root_device = self.default_root_device self.virtapi.instance_update( nova_context.get_admin_context(), instance['uuid'], - {'root_device_name': '/dev/' + self.default_root_device}) + {'root_device_name': '/dev/' + disk_mapping['disk']['dev']}) guest.os_type = vm_mode.get_from_instance(instance) @@ -1841,10 +1817,10 @@ class LibvirtDriver(driver.ComputeDriver): elif CONF.libvirt_type == "uml": guest.os_type = vm_mode.UML guest.os_kernel = "/usr/bin/linux" - guest.os_root = root_device_name or "/dev/ubda" + guest.os_root = root_device_name else: if CONF.libvirt_type == "xen" and guest.os_type == vm_mode.XEN: - guest.os_root = root_device_name or "/dev/xvda" + guest.os_root = root_device_name else: guest.os_type = vm_mode.HVM @@ -1855,7 +1831,7 @@ class LibvirtDriver(driver.ComputeDriver): guest.os_cmdline = "ro" else: guest.os_cmdline = ("root=%s console=ttyS0" % - (root_device_name or "/dev/vda",)) + root_device_name) if rescue.get('ramdisk_id'): guest.os_initrd = os.path.join(inst_path, "ramdisk.rescue") @@ -1865,7 +1841,7 @@ class LibvirtDriver(driver.ComputeDriver): guest.os_cmdline = "ro" else: guest.os_cmdline = ("root=%s console=ttyS0" % - (root_device_name or "/dev/vda",)) + root_device_name) if instance['ramdisk_id']: guest.os_initrd = os.path.join(inst_path, "ramdisk") else: @@ -1895,11 +1871,10 @@ class LibvirtDriver(driver.ComputeDriver): for cfg in self.get_guest_storage_config(instance, image_meta, + disk_info, rescue, block_device_info, - inst_type, - root_device_name, - root_device): + inst_type): guest.add_device(cfg) for (network, mapping) in network_info: @@ -1971,11 +1946,17 @@ class LibvirtDriver(driver.ComputeDriver): return guest - def to_xml(self, instance, network_info, image_meta=None, rescue=None, + def to_xml(self, instance, network_info, disk_info, + image_meta=None, rescue=None, block_device_info=None, write_to_disk=False): - LOG.debug(_('Starting toXML method'), instance=instance) + LOG.debug(_("Start to_xml instance=%(instance)s " + "network_info=%(network_info)s " + "disk_info=%(disk_info)s " + "image_meta=%(image_meta)s rescue=%(rescue)s" + "block_device_info=%(block_device_info)s") % + locals()) conf = self.get_guest_config(instance, network_info, image_meta, - rescue, block_device_info) + disk_info, rescue, block_device_info) xml = conf.to_xml() if write_to_disk: @@ -1984,7 +1965,7 @@ class LibvirtDriver(driver.ComputeDriver): xml_path = os.path.join(instance_dir, 'libvirt.xml') libvirt_utils.write_to_file(xml_path, xml) - LOG.debug(_('Finished toXML method'), instance=instance) + LOG.debug(_('End to_xml instance=%(instance)s xml=%(xml)s') % locals()) return xml def _lookup_by_name(self, instance_name): @@ -2066,10 +2047,16 @@ class LibvirtDriver(driver.ComputeDriver): for vol in block_device_mapping: connection_info = vol['connection_info'] - mount_device = vol['mount_device'].rpartition("/")[2] + 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', + } self.volume_driver_method('connect_volume', connection_info, - mount_device) + disk_info) self.plug_vifs(instance, network_info) self.firewall_driver.setup_basic_filtering(instance, network_info) @@ -2811,10 +2798,16 @@ class LibvirtDriver(driver.ComputeDriver): block_device_info) for vol in block_device_mapping: connection_info = vol['connection_info'] - mount_device = vol['mount_device'].rpartition("/")[2] + 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', + } self.volume_driver_method('connect_volume', connection_info, - mount_device) + disk_info) # We call plug_vifs before the compute manager calls # ensure_filtering_rules_for_instance, to ensure bridge is set up @@ -2901,8 +2894,10 @@ class LibvirtDriver(driver.ComputeDriver): if instance_ref["name"] not in dom_list: # In case of block migration, destination does not have # libvirt.xml - self.to_xml(instance_ref, network_info, block_device_info, - write_to_disk=True) + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance_ref) + self.to_xml(instance_ref, network_info, disk_info, + block_device_info, write_to_disk=True) # libvirt.xml should be made by to_xml(), but libvirt # does not accept to_xml() result, since uuid is not # included in to_xml() result. @@ -3055,10 +3050,10 @@ class LibvirtDriver(driver.ComputeDriver): block_device_info) for vol in block_device_mapping: connection_info = vol['connection_info'] - mount_device = vol['mount_device'].rpartition("/")[2] + disk_dev = vol['mount_device'].rpartition("/")[2] self.volume_driver_method('disconnect_volume', connection_info, - mount_device) + disk_dev) # copy disks to destination # rename instance dir to +_resize at first for using @@ -3145,13 +3140,18 @@ class LibvirtDriver(driver.ComputeDriver): '-O', 'qcow2', info['path'], path_qcow) utils.execute('mv', path_qcow, info['path']) - xml = self.to_xml(instance, network_info, + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance, + block_device_info, + image_meta) + xml = self.to_xml(instance, network_info, disk_info, block_device_info=block_device_info) # assume _create_image do nothing if a target file exists. # TODO(oda): injecting files is not necessary self._create_image(context, instance, xml, - network_info=network_info, - block_device_info=None) + disk_mapping=disk_info['mapping'], + network_info=network_info, + block_device_info=None) self._create_domain_and_network(xml, instance, network_info, block_device_info) timer = utils.FixedIntervalLoopingCall(self._wait_for_running, @@ -3167,7 +3167,10 @@ class LibvirtDriver(driver.ComputeDriver): inst_base_resize = inst_base + "_resize" utils.execute('mv', inst_base_resize, inst_base) - xml = self.to_xml(instance, network_info, + disk_info = blockinfo.get_disk_info(CONF.libvirt_type, + instance, + block_device_info) + xml = self.to_xml(instance, network_info, disk_info, block_device_info=block_device_info) self._create_domain_and_network(xml, instance, network_info, block_device_info) diff --git a/nova/virt/libvirt/volume.py b/nova/virt/libvirt/volume.py index f8ed95e0f..724a859b8 100644 --- a/nova/virt/libvirt/volume.py +++ b/nova/virt/libvirt/volume.py @@ -62,19 +62,20 @@ class LibvirtBaseVolumeDriver(object): self.connection = connection self.is_block_dev = is_block_dev - def connect_volume(self, connection_info, mount_device): + def connect_volume(self, connection_info, disk_info): """Connect the volume. Returns xml for libvirt.""" conf = vconfig.LibvirtConfigGuestDisk() conf.driver_name = virtutils.pick_disk_driver_name(self.is_block_dev) + conf.device_type = disk_info['type'] conf.driver_format = "raw" conf.driver_cache = "none" - conf.target_dev = mount_device - conf.target_bus = "virtio" + conf.target_dev = disk_info['dev'] + conf.target_bus = disk_info['bus'] conf.serial = connection_info.get('serial') return conf - def disconnect_volume(self, connection_info, mount_device): + def disconnect_volume(self, connection_info, disk_dev): """Disconnect the volume.""" pass @@ -85,10 +86,11 @@ class LibvirtVolumeDriver(LibvirtBaseVolumeDriver): super(LibvirtVolumeDriver, self).__init__(connection, is_block_dev=True) - def connect_volume(self, connection_info, mount_device): + def connect_volume(self, connection_info, disk_info): """Connect the volume to a local device.""" conf = super(LibvirtVolumeDriver, - self).connect_volume(connection_info, mount_device) + self).connect_volume(connection_info, + disk_info) conf.source_type = "block" conf.source_path = connection_info['data']['device_path'] return conf @@ -100,10 +102,11 @@ class LibvirtFakeVolumeDriver(LibvirtBaseVolumeDriver): super(LibvirtFakeVolumeDriver, self).__init__(connection, is_block_dev=True) - def connect_volume(self, connection_info, mount_device): + def connect_volume(self, connection_info, disk_info): """Connect the volume to a fake device.""" conf = super(LibvirtFakeVolumeDriver, - self).connect_volume(connection_info, mount_device) + self).connect_volume(connection_info, + disk_info) conf.source_type = "network" conf.source_protocol = "fake" conf.source_host = "fake" @@ -116,9 +119,10 @@ class LibvirtNetVolumeDriver(LibvirtBaseVolumeDriver): super(LibvirtNetVolumeDriver, self).__init__(connection, is_block_dev=False) - def connect_volume(self, connection_info, mount_device): + def connect_volume(self, connection_info, disk_info): conf = super(LibvirtNetVolumeDriver, - self).connect_volume(connection_info, mount_device) + self).connect_volume(connection_info, + disk_info) conf.source_type = "network" conf.source_protocol = connection_info['driver_volume_type'] conf.source_host = connection_info['data']['name'] @@ -163,10 +167,11 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver): return self._run_iscsiadm(iscsi_properties, iscsi_command, **kwargs) @lockutils.synchronized('connect_volume', 'nova-') - def connect_volume(self, connection_info, mount_device): + def connect_volume(self, connection_info, disk_info): """Attach the volume to instance_name.""" conf = super(LibvirtISCSIVolumeDriver, - self).connect_volume(connection_info, mount_device) + self).connect_volume(connection_info, + disk_info) iscsi_properties = connection_info['data'] # NOTE(vish): If we are on the same host as nova volume, the @@ -210,12 +215,13 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver): # The /dev/disk/by-path/... node is not always present immediately # TODO(justinsb): This retry-with-delay is a pattern, move to utils? tries = 0 + disk_dev = disk_info['dev'] while not os.path.exists(host_device): if tries >= CONF.num_iscsi_scan_tries: raise exception.NovaException(_("iSCSI device not found at %s") % (host_device)) - LOG.warn(_("ISCSI volume not yet found at: %(mount_device)s. " + LOG.warn(_("ISCSI volume not yet found at: %(disk_dev)s. " "Will rescan & retry. Try number: %(tries)s") % locals()) @@ -227,7 +233,7 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver): time.sleep(tries ** 2) if tries != 0: - LOG.debug(_("Found iSCSI node %(mount_device)s " + LOG.debug(_("Found iSCSI node %(disk_dev)s " "(after %(tries)s rescans)") % locals()) @@ -236,10 +242,10 @@ class LibvirtISCSIVolumeDriver(LibvirtBaseVolumeDriver): return conf @lockutils.synchronized('connect_volume', 'nova-') - def disconnect_volume(self, connection_info, mount_device): + def disconnect_volume(self, connection_info, disk_dev): """Detach the volume from instance_name.""" super(LibvirtISCSIVolumeDriver, - self).disconnect_volume(connection_info, mount_device) + self).disconnect_volume(connection_info, disk_dev) iscsi_properties = connection_info['data'] # NOTE(vish): Only disconnect from the target if no luns from the # target are in use. @@ -265,10 +271,11 @@ class LibvirtNFSVolumeDriver(LibvirtBaseVolumeDriver): super(LibvirtNFSVolumeDriver, self).__init__(connection, is_block_dev=False) - def connect_volume(self, connection_info, mount_device): + def connect_volume(self, connection_info, disk_info): """Connect the volume. Returns xml for libvirt.""" conf = super(LibvirtNFSVolumeDriver, - self).connect_volume(connection_info, mount_device) + self).connect_volume(connection_info, + disk_info) path = self._ensure_mounted(connection_info['data']['export']) path = os.path.join(path, connection_info['data']['name']) conf.source_type = 'file' |