Merge "Libvirt driver implementation of device tagging"
This commit is contained in:
@@ -67,6 +67,7 @@ from nova.network import model as network_model
|
||||
from nova import objects
|
||||
from nova.objects import block_device as block_device_obj
|
||||
from nova.objects import fields
|
||||
from nova.objects import virtual_interface as obj_vif
|
||||
from nova.pci import manager as pci_manager
|
||||
from nova.pci import utils as pci_utils
|
||||
from nova import test
|
||||
@@ -1433,6 +1434,156 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
self.assertRaises(exception.PciDevicePrepareFailed,
|
||||
drvr._prepare_pci_devices_for_use, pci_devices)
|
||||
|
||||
@mock.patch.object(host.Host, "has_min_version", return_value=False)
|
||||
def test_device_metadata(self, mock_version):
|
||||
xml = """
|
||||
<domain>
|
||||
<name>dummy</name>
|
||||
<uuid>32dfcb37-5af1-552b-357c-be8c3aa38310</uuid>
|
||||
<memory>1048576</memory>
|
||||
<vcpu>1</vcpu>
|
||||
<os>
|
||||
<type arch='x86_64' machine='pc-i440fx-2.4'>hvm</type>
|
||||
</os>
|
||||
<devices>
|
||||
<disk type='block' device='disk'>
|
||||
<driver name='qemu' type='qcow2'/>
|
||||
<source dev='/dev/mapper/generic'/>
|
||||
<target dev='sda' bus='scsi'/>
|
||||
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
|
||||
</disk>
|
||||
<disk type='block' device='disk'>
|
||||
<driver name='qemu' type='qcow2'/>
|
||||
<source dev='/dev/mapper/generic-1'/>
|
||||
<target dev='hda' bus='ide'/>
|
||||
<address type='drive' controller='0' bus='1' target='0' unit='0'/>
|
||||
</disk>
|
||||
<disk type='block' device='disk'>
|
||||
<driver name='qemu' type='qcow2'/>
|
||||
<source dev='/dev/mapper/generic-2'/>
|
||||
<target dev='hdb' bus='ide'/>
|
||||
<address type='drive' controller='0' bus='1' target='1' unit='1'/>
|
||||
</disk>
|
||||
<disk type='block' device='disk'>
|
||||
<driver name='qemu' type='qcow2'/>
|
||||
<source dev='/dev/mapper/aa1'/>
|
||||
<target dev='sdb' bus='usb'/>
|
||||
</disk>
|
||||
<disk type='block' device='disk'>
|
||||
<driver name='qemu' type='qcow2'/>
|
||||
<source dev='/var/lib/libvirt/images/centos'/>
|
||||
<backingStore/>
|
||||
<target dev='vda' bus='virtio'/>
|
||||
<boot order='1'/>
|
||||
<alias name='virtio-disk0'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x09'
|
||||
function='0x0'/>
|
||||
</disk>
|
||||
<interface type='network'>
|
||||
<mac address='52:54:00:f6:35:8f'/>
|
||||
<source network='default'/>
|
||||
<model type='virtio'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x03'
|
||||
function='0x0'/>
|
||||
</interface>
|
||||
<interface type='network'>
|
||||
<mac address='51:5a:2c:a4:5e:1b'/>
|
||||
<source network='default'/>
|
||||
<model type='virtio'/>
|
||||
<address type='pci' domain='0x0000' bus='0x00' slot='0x04'
|
||||
function='0x1'/>
|
||||
</interface>
|
||||
</devices>
|
||||
</domain>"""
|
||||
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
dom = fakelibvirt.Domain(drvr._get_connection(), xml, False)
|
||||
guest = libvirt_guest.Guest(dom)
|
||||
|
||||
instance_ref = objects.Instance(**self.test_instance)
|
||||
bdms = block_device_obj.block_device_make_list_from_dicts(
|
||||
self.context, [
|
||||
fake_block_device.FakeDbBlockDeviceDict(
|
||||
{'id': 1,
|
||||
'source_type': 'volume', 'destination_type': 'volume',
|
||||
'device_name': '/dev/sda', 'tag': "db"}),
|
||||
fake_block_device.FakeDbBlockDeviceDict(
|
||||
{'id': 2,
|
||||
'source_type': 'volume', 'destination_type': 'volume',
|
||||
'device_name': '/dev/hda', 'tag': "nfvfunc1"}),
|
||||
fake_block_device.FakeDbBlockDeviceDict(
|
||||
{'id': 3,
|
||||
'source_type': 'volume', 'destination_type': 'volume',
|
||||
'device_name': '/dev/sdb', 'tag': "nfvfunc2"}),
|
||||
fake_block_device.FakeDbBlockDeviceDict(
|
||||
{'id': 4,
|
||||
'source_type': 'volume', 'destination_type': 'volume',
|
||||
'device_name': '/dev/hdb'}),
|
||||
fake_block_device.FakeDbBlockDeviceDict(
|
||||
{'id': 5,
|
||||
'source_type': 'volume', 'destination_type': 'volume',
|
||||
'device_name': '/dev/vda', 'tag': "nfvfunc3"}),
|
||||
]
|
||||
)
|
||||
vif = obj_vif.VirtualInterface(context=self.context)
|
||||
vif.address = '52:54:00:f6:35:8f'
|
||||
vif.network_id = 123
|
||||
vif.instance_uuid = '32dfcb37-5af1-552b-357c-be8c3aa38310'
|
||||
vif.uuid = '12ec4b21-ef22-6c21-534b-ba3e3ab3a311'
|
||||
vif.tag = 'mytag1'
|
||||
vif1 = obj_vif.VirtualInterface(context=self.context)
|
||||
vif1.address = '51:5a:2c:a4:5e:1b'
|
||||
vif1.network_id = 123
|
||||
vif1.instance_uuid = '32dfcb37-5af1-552b-357c-be8c3aa38310'
|
||||
vif1.uuid = 'abec4b21-ef22-6c21-534b-ba3e3ab3a312'
|
||||
vif1.tag = None
|
||||
vifs = [vif, vif1]
|
||||
|
||||
with test.nested(
|
||||
mock.patch('nova.objects.VirtualInterfaceList'
|
||||
'.get_by_instance_uuid', return_value=vifs),
|
||||
mock.patch('nova.objects.BlockDeviceMappingList'
|
||||
'.get_by_instance_uuid', return_value=bdms),
|
||||
mock.patch('nova.virt.libvirt.host.Host.get_guest',
|
||||
return_value=guest),
|
||||
mock.patch.object(nova.virt.libvirt.guest.Guest, 'get_xml_desc',
|
||||
return_value=xml)):
|
||||
metadata_obj = drvr._build_device_metadata(self.context,
|
||||
instance_ref)
|
||||
metadata = metadata_obj.devices
|
||||
self.assertEqual(5, len(metadata))
|
||||
self.assertIsInstance(metadata[0],
|
||||
objects.DiskMetadata)
|
||||
self.assertIsInstance(metadata[0].bus,
|
||||
objects.SCSIDeviceBus)
|
||||
self.assertEqual(['db'], metadata[0].tags)
|
||||
self.assertFalse(metadata[0].bus.obj_attr_is_set('address'))
|
||||
self.assertEqual(['nfvfunc1'], metadata[1].tags)
|
||||
self.assertIsInstance(metadata[1],
|
||||
objects.DiskMetadata)
|
||||
self.assertIsInstance(metadata[1].bus,
|
||||
objects.IDEDeviceBus)
|
||||
self.assertEqual(['nfvfunc1'], metadata[1].tags)
|
||||
self.assertFalse(metadata[1].bus.obj_attr_is_set('address'))
|
||||
self.assertIsInstance(metadata[2],
|
||||
objects.DiskMetadata)
|
||||
self.assertIsInstance(metadata[2].bus,
|
||||
objects.USBDeviceBus)
|
||||
self.assertEqual(['nfvfunc2'], metadata[2].tags)
|
||||
self.assertFalse(metadata[2].bus.obj_attr_is_set('address'))
|
||||
self.assertIsInstance(metadata[3],
|
||||
objects.DiskMetadata)
|
||||
self.assertIsInstance(metadata[3].bus,
|
||||
objects.PCIDeviceBus)
|
||||
self.assertEqual(['nfvfunc3'], metadata[3].tags)
|
||||
self.assertEqual('0000:00:09.0', metadata[3].bus.address)
|
||||
self.assertIsInstance(metadata[4],
|
||||
objects.NetworkInterfaceMetadata)
|
||||
self.assertIsInstance(metadata[4].bus,
|
||||
objects.PCIDeviceBus)
|
||||
self.assertEqual(['mytag1'], metadata[4].tags)
|
||||
self.assertEqual('0000:00:03.0', metadata[4].bus.address)
|
||||
|
||||
@mock.patch.object(host.Host, 'get_connection')
|
||||
@mock.patch.object(nova.virt.libvirt.guest.Guest, 'get_xml_desc')
|
||||
def test_detach_pci_devices(self, mocked_get_xml_desc, mock_conn):
|
||||
@@ -9402,7 +9553,9 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
self.assertEqual(info[0]['backing_file'], "")
|
||||
self.assertEqual(info[0]['over_committed_disk_size'], 0)
|
||||
|
||||
def test_spawn_with_network_info(self):
|
||||
@mock.patch('nova.virt.configdrive.required_by',
|
||||
return_value=False)
|
||||
def test_spawn_with_network_info(self, config_mock):
|
||||
# Preparing mocks
|
||||
def fake_none(*args, **kwargs):
|
||||
return
|
||||
@@ -9448,6 +9601,10 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
instance = objects.Instance(**instance_ref)
|
||||
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
|
||||
|
||||
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver,
|
||||
'_build_device_metadata')
|
||||
libvirt_driver.LibvirtDriver._build_device_metadata(self.context,
|
||||
instance)
|
||||
# Mock out the get_info method of the LibvirtDriver so that the polling
|
||||
# in the spawn method of the LibvirtDriver returns immediately
|
||||
self.mox.StubOutWithMock(libvirt_driver.LibvirtDriver, 'get_info')
|
||||
@@ -9968,7 +10125,10 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
]
|
||||
self.assertEqual(gotFiles, wantFiles)
|
||||
|
||||
def test_create_configdrive(self):
|
||||
@mock.patch(
|
||||
'nova.virt.libvirt.driver.LibvirtDriver._build_device_metadata',
|
||||
return_value=None)
|
||||
def test_create_configdrive(self, mock_save):
|
||||
def enable_configdrive(instance_ref):
|
||||
instance_ref['config_drive'] = 'true'
|
||||
|
||||
@@ -9998,6 +10158,8 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
with test.nested(
|
||||
mock.patch.object(drvr, '_create_images_and_backing'),
|
||||
mock.patch.object(drvr, 'plug_vifs'),
|
||||
mock.patch.object(drvr, '_build_device_metadata',
|
||||
return_value=None),
|
||||
mock.patch.object(drvr.firewall_driver, 'setup_basic_filtering'),
|
||||
mock.patch.object(drvr.firewall_driver, 'prepare_instance_filter'),
|
||||
mock.patch.object(drvr.firewall_driver, 'apply_instance_filter')):
|
||||
@@ -15762,11 +15924,15 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
|
||||
self.assertEqual(rescue_file, mock_del.call_args_list[1][0][0])
|
||||
mock_remove_volumes.assert_called_once_with(['lvm.rescue'])
|
||||
|
||||
@mock.patch(
|
||||
'nova.virt.libvirt.driver.LibvirtDriver._build_device_metadata',
|
||||
return_value=None)
|
||||
@mock.patch(
|
||||
'nova.virt.configdrive.ConfigDriveBuilder.add_instance_metadata')
|
||||
@mock.patch('nova.virt.configdrive.ConfigDriveBuilder.make_drive')
|
||||
@mock.patch('nova.virt.libvirt.guest.Guest')
|
||||
def test_rescue_config_drive(self, mock_guest, mock_make, mock_add):
|
||||
def test_rescue_config_drive(self, mock_guest, mock_make, mock_add,
|
||||
mock_save):
|
||||
instance = self._create_instance()
|
||||
uuid = instance.uuid
|
||||
configdrive_path = uuid + '/disk.config.rescue'
|
||||
|
||||
@@ -136,7 +136,8 @@ class ComputeDriver(object):
|
||||
"has_imagecache": False,
|
||||
"supports_recreate": False,
|
||||
"supports_migrate_to_same_host": False,
|
||||
"supports_attach_interface": False
|
||||
"supports_attach_interface": False,
|
||||
"supports_device_tagging": False,
|
||||
}
|
||||
|
||||
def __init__(self, virtapi):
|
||||
|
||||
@@ -320,7 +320,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
"has_imagecache": True,
|
||||
"supports_recreate": True,
|
||||
"supports_migrate_to_same_host": False,
|
||||
"supports_attach_interface": True
|
||||
"supports_attach_interface": True,
|
||||
"supports_device_tagging": True,
|
||||
}
|
||||
|
||||
def __init__(self, virtapi, read_only=False):
|
||||
@@ -3100,6 +3101,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
|
||||
def _create_configdrive(self, context, instance, admin_pass=None,
|
||||
files=None, network_info=None, suffix=''):
|
||||
# As this method being called right after the definition of a
|
||||
# domain, but before its actual launch, device metadata will be built
|
||||
# and saved in the instance for it to be used by the config drive and
|
||||
# the metadata service.
|
||||
instance.device_metadata = self._build_device_metadata(context,
|
||||
instance)
|
||||
config_drive_image = None
|
||||
if configdrive.required_by(instance):
|
||||
LOG.info(_LI('Using config drive'), instance=instance)
|
||||
@@ -7445,6 +7452,72 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
diags.nic_details[index].mac_address = node.get('address')
|
||||
return diags
|
||||
|
||||
@staticmethod
|
||||
def _prepare_device_bus(dev):
|
||||
"""Determins the device bus and it's hypervisor assigned address
|
||||
"""
|
||||
address = (dev.device_addr.format_address() if
|
||||
dev.device_addr else None)
|
||||
if isinstance(dev.device_addr,
|
||||
vconfig.LibvirtConfigGuestDeviceAddressPCI):
|
||||
bus = objects.PCIDeviceBus()
|
||||
elif dev.target_bus == 'scsi':
|
||||
bus = objects.SCSIDeviceBus()
|
||||
elif dev.target_bus == 'ide':
|
||||
bus = objects.IDEDeviceBus()
|
||||
elif dev.target_bus == 'usb':
|
||||
bus = objects.USBDeviceBus()
|
||||
if address is not None:
|
||||
bus.address = address
|
||||
return bus
|
||||
|
||||
def _build_device_metadata(self, context, instance):
|
||||
"""Builds a metadata object for instance devices, that maps the user
|
||||
provided tag to the hypervisor assigned device address.
|
||||
"""
|
||||
def _get_device_name(bdm):
|
||||
return block_device.strip_dev(bdm.device_name)
|
||||
|
||||
vifs = objects.VirtualInterfaceList.get_by_instance_uuid(context,
|
||||
instance.uuid)
|
||||
tagged_vifs = {vif.address: vif for vif in vifs if vif.tag}
|
||||
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
|
||||
context, instance.uuid)
|
||||
tagged_bdms = {_get_device_name(bdm): bdm for bdm in bdms if bdm.tag}
|
||||
|
||||
devices = []
|
||||
guest = self._host.get_guest(instance)
|
||||
xml = guest.get_xml_desc()
|
||||
xml_dom = etree.fromstring(xml)
|
||||
guest_config = vconfig.LibvirtConfigGuest()
|
||||
guest_config.parse_dom(xml_dom)
|
||||
|
||||
for dev in guest_config.devices:
|
||||
# Build network intefaces related metedata
|
||||
if isinstance(dev, vconfig.LibvirtConfigGuestInterface):
|
||||
vif = tagged_vifs.get(dev.mac_addr)
|
||||
if not vif:
|
||||
continue
|
||||
bus = self._prepare_device_bus(dev)
|
||||
device = objects.NetworkInterfaceMetadata(
|
||||
mac=vif.address,
|
||||
bus=bus,
|
||||
tags=[vif.tag]
|
||||
)
|
||||
devices.append(device)
|
||||
|
||||
# Build disks related metedata
|
||||
if isinstance(dev, vconfig.LibvirtConfigGuestDisk):
|
||||
bdm = tagged_bdms.get(dev.target_dev)
|
||||
if not bdm:
|
||||
continue
|
||||
bus = self._prepare_device_bus(dev)
|
||||
device = objects.DiskMetadata(bus=bus, tags=[bdm.tag])
|
||||
devices.append(device)
|
||||
if devices:
|
||||
dev_meta = objects.InstanceDeviceMetadata(devices=devices)
|
||||
return dev_meta
|
||||
|
||||
def instance_on_disk(self, instance):
|
||||
# ensure directories exist and are writable
|
||||
instance_path = libvirt_utils.get_instance_path(instance)
|
||||
|
||||
Reference in New Issue
Block a user