libvirt: allow querying devices from the persistent domain

This patch adds from_persistent_config kwargs to get_interface_by_cfg()
and get_disk() so that the caller can specify which domain config the
devices is read from. Currently, if there was both a live domain and a
persistent domain then nova only reads from the live domain. In a later
patch during device detach these calls will be used to detach from the
persistent domain separately from the live domain.

Change-Id: I86153d31b02e6b74b42d53a6800297cbd0e5cbb4
Related-Bug: #1882521
This commit is contained in:
Balazs Gibizer 2021-01-25 17:54:46 +01:00
parent f5cd6e2dea
commit a634103b15
3 changed files with 97 additions and 7 deletions

View File

@ -24034,6 +24034,41 @@ class LibvirtDriverTestCase(test.NoDBTestCase, TraitsComparisonMixin):
self.assertIsNone(guest.get_disk('vdc'))
dom.XMLDesc.assert_has_calls([mock.call(0)] * 3)
def test_get_disk_xml_from_persistent_config(self):
dom_xml = """
<domain type="kvm">
<devices>
<disk type="file">
<source file="disk1_file"/>
<target dev="vda" bus="virtio"/>
<serial>0e38683e-f0af-418f-a3f1-6b67ea0f919d</serial>
</disk>
<disk type="block">
<source dev="/path/to/dev/1"/>
<target dev="vdb" bus="virtio" serial="1234"/>
</disk>
</devices>
</domain>
"""
diska_xml = """<disk type="file" device="disk">
<source file="disk1_file"/>
<target bus="virtio" dev="vda"/>
<serial>0e38683e-f0af-418f-a3f1-6b67ea0f919d</serial>
</disk>"""
dom = mock.MagicMock()
dom.XMLDesc.return_value = dom_xml
guest = libvirt_guest.Guest(dom)
actual_diska_xml = guest.get_disk(
'vda', from_persistent_config=True).to_xml()
self.assertXmlEqual(diska_xml, actual_diska_xml)
dom.XMLDesc.assert_called_once_with(
fakelibvirt.VIR_DOMAIN_XML_INACTIVE)
def test_vcpu_model_from_config(self):
drv = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
vcpu_model = drv._cpu_config_to_vcpu_model(None, None)

View File

@ -540,6 +540,49 @@ class GuestTestCase(test.NoDBTestCase):
self.assertIsNotNone(
self.guest.get_interface_by_cfg(cfg))
self.assertIsNone(self.guest.get_interface_by_cfg(None))
self.domain.XMLDesc.assert_has_calls([mock.call(0)] * 6)
# now check if the persistent config can be queried too
self.domain.XMLDesc.reset_mock()
devs = self.guest.get_all_devices(
devtype=None, from_persistent_config=True)
self.domain.XMLDesc.assert_called_once_with(
fakelibvirt.VIR_DOMAIN_XML_INACTIVE)
def test_get_interface_by_cfg_persistent_domain(self):
self.domain.XMLDesc.return_value = """<domain>
<devices>
<interface type="bridge">
<mac address="fa:16:3e:f9:af:ae"/>
<model type="virtio"/>
<driver name="qemu"/>
<source bridge="qbr84008d03-11"/>
<target dev="tap84008d03-11"/>
</interface>
</devices>
</domain>"""
cfg = vconfig.LibvirtConfigGuestInterface()
cfg.parse_str("""
<interface type="bridge">
<mac address="fa:16:3e:f9:af:ae"/>
<model type="virtio"/>
<driver name="qemu"/>
<source bridge="qbr84008d03-11"/>
<target dev="tap84008d03-11"/>
</interface>""")
self.assertIsNotNone(
self.guest.get_interface_by_cfg(
cfg, from_persistent_config=True))
self.assertIsNone(
self.guest.get_interface_by_cfg(
vconfig.LibvirtConfigGuestInterface(),
from_persistent_config=True))
self.domain.XMLDesc.assert_has_calls(
[
mock.call(fakelibvirt.VIR_DOMAIN_XML_INACTIVE),
mock.call(fakelibvirt.VIR_DOMAIN_XML_INACTIVE),
]
)
def test_get_interface_by_cfg_vhostuser(self):
self.domain.XMLDesc.return_value = """<domain>

View File

@ -227,19 +227,22 @@ class Guest(object):
return interfaces
def get_interface_by_cfg(self, cfg):
def get_interface_by_cfg(self, cfg, from_persistent_config=False):
"""Lookup a full LibvirtConfigGuestDevice with
LibvirtConfigGuesDevice generated
by nova.virt.libvirt.vif.get_config.
:param cfg: config object that represents the guest interface.
:type cfg: a subtype of LibvirtConfigGuestDevice object
:param from_persistent_config: query the device from the persistent
domain instead of the live domain configuration
:returns: nova.virt.libvirt.config.LibvirtConfigGuestDevice instance
if found, else None
"""
if cfg:
interfaces = self.get_all_devices(type(cfg))
interfaces = self.get_all_devices(
type(cfg), from_persistent_config)
for interface in interfaces:
# NOTE(leehom) LibvirtConfigGuest get from domain and
# LibvirtConfigGuest generated by
@ -312,13 +315,16 @@ class Guest(object):
config.parse_str(self._domain.XMLDesc(0))
return config
def get_disk(self, device):
def get_disk(self, device, from_persistent_config=False):
"""Returns the disk mounted at device
:returns LivirtConfigGuestDisk: mounted at device or None
"""
flags = 0
if from_persistent_config:
flags |= libvirt.VIR_DOMAIN_XML_INACTIVE
try:
doc = etree.fromstring(self._domain.XMLDesc(0))
doc = etree.fromstring(self._domain.XMLDesc(flags))
except Exception:
return None
@ -343,18 +349,24 @@ class Guest(object):
return self.get_all_devices(vconfig.LibvirtConfigGuestDisk)
def get_all_devices(self, devtype=None):
def get_all_devices(self, devtype=None, from_persistent_config=False):
"""Returns all devices for a guest
:param devtype: a LibvirtConfigGuestDevice subclass class
:param from_persistent_config: query the device from the persistent
domain (i.e. inactive XML configuration that'll be used on next
start of the domain) instead of the live domain configuration
:returns: a list of LibvirtConfigGuestDevice instances
"""
flags = 0
if from_persistent_config:
flags |= libvirt.VIR_DOMAIN_XML_INACTIVE
try:
config = vconfig.LibvirtConfigGuest()
config.parse_str(
self._domain.XMLDesc(0))
self._domain.XMLDesc(flags))
except Exception:
return []