Read current domain state from XML on filesystem

Use the XML file as a source of truth when getting current
state of each domain. Reading current state from the running
VM's requires that we do a reboot every time we change something.

Change-Id: Ifcc9afc46562a5df5dd73010b8d274452c1aefc1
Story: #2007735
Task: #39901
This commit is contained in:
Derek Higgins 2020-06-03 14:33:41 +01:00
parent 6baebba032
commit 13a0b5920c
3 changed files with 27 additions and 56 deletions

View File

@ -0,0 +1,11 @@
---
issues:
- |
Reads hardware state from libvirt domain XML
Now reads boot device/mode/image from domain XML on
filesystem rather then the running VM, thus avoiding
the need for a reboot after setting soemthing. The client
should now power cycle the instance if the changes are
required in the running VM.
note: not simply a soft reboot.

View File

@ -62,23 +62,6 @@ class libvirt_open(object):
self._conn.close()
def power_cycle(wrapped):
def wrapper(self, identity, *args, **kwargs):
power_state = self.get_power_state(identity)
if power_state == 'On':
self.set_power_state(identity, 'ForceOff')
try:
return wrapped(self, identity, *args, **kwargs)
finally:
if power_state == 'On':
self.set_power_state(identity, power_state)
return wrapper
class LibvirtDriver(AbstractSystemsDriver):
"""Libvirt driver"""
@ -301,7 +284,7 @@ class LibvirtDriver(AbstractSystemsDriver):
"""
domain = self._get_domain(identity, readonly=True)
tree = ET.fromstring(domain.XMLDesc())
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
# Try boot configuration in the bootloader
@ -364,7 +347,6 @@ class LibvirtDriver(AbstractSystemsDriver):
return boot_source_target
@power_cycle
def set_boot_device(self, identity, boot_source):
"""Get/Set computer system boot device name
@ -382,7 +364,7 @@ class LibvirtDriver(AbstractSystemsDriver):
domain = self._get_domain(identity)
# XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS
tree = ET.fromstring(domain.XMLDesc())
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
# Remove bootloader configuration
@ -463,7 +445,7 @@ class LibvirtDriver(AbstractSystemsDriver):
domain = self._get_domain(identity, readonly=True)
# XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS
tree = ET.fromstring(domain.XMLDesc())
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
loader_element = tree.find('.//loader')
@ -474,7 +456,6 @@ class LibvirtDriver(AbstractSystemsDriver):
return boot_mode
@power_cycle
def set_boot_mode(self, identity, boot_mode):
"""Set computer system boot mode.
@ -486,7 +467,7 @@ class LibvirtDriver(AbstractSystemsDriver):
domain = self._get_domain(identity, readonly=True)
# XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS
tree = ET.fromstring(domain.XMLDesc())
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
try:
loader_type = self.BOOT_MODE_MAP[boot_mode]
@ -593,7 +574,7 @@ class LibvirtDriver(AbstractSystemsDriver):
"""
domain = self._get_domain(identity, readonly=True)
tree = ET.fromstring(domain.XMLDesc())
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
total_cpus = 0
@ -692,9 +673,10 @@ class LibvirtDriver(AbstractSystemsDriver):
"""
domain = self._get_domain(identity)
result = self._process_bios_attributes(domain.XMLDesc(),
bios_attributes,
update_existing_attributes)
result = self._process_bios_attributes(
domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE),
bios_attributes,
update_existing_attributes)
if result.attributes_written:
@ -720,7 +702,6 @@ class LibvirtDriver(AbstractSystemsDriver):
"""
return self._process_bios(identity)
@power_cycle
def set_bios(self, identity, attributes):
"""Update BIOS attributes
@ -743,7 +724,6 @@ class LibvirtDriver(AbstractSystemsDriver):
self._process_bios(identity, bios_attributes,
update_existing_attributes=True)
@power_cycle
def reset_bios(self, identity):
"""Reset BIOS attributes to default
@ -762,7 +742,7 @@ class LibvirtDriver(AbstractSystemsDriver):
:returns: list of network interfaces dict with their attributes
"""
domain = self._get_domain(identity, readonly=True)
tree = ET.fromstring(domain.XMLDesc())
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
return [{'id': iface.get('address'), 'mac': iface.get('address')}
for iface in tree.findall(
".//devices/interface[@type='network']/mac")]
@ -778,7 +758,7 @@ class LibvirtDriver(AbstractSystemsDriver):
"""
domain = self._get_domain(identity, readonly=True)
tree = ET.fromstring(domain.XMLDesc())
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
device_element = tree.find('devices')
if device_element is None:
@ -970,7 +950,6 @@ class LibvirtDriver(AbstractSystemsDriver):
if dev_type == lv_device:
device_element.remove(disk_element)
@power_cycle
def set_boot_image(self, identity, device, boot_image=None,
write_protected=True):
"""Set backend VM boot image
@ -986,7 +965,8 @@ class LibvirtDriver(AbstractSystemsDriver):
"""
domain = self._get_domain(identity)
domain_tree = ET.fromstring(domain.XMLDesc())
domain_tree = ET.fromstring(
domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
self._remove_boot_images(domain, domain_tree, device)
@ -1082,7 +1062,7 @@ class LibvirtDriver(AbstractSystemsDriver):
:returns: dict of simple storage controller dict with their attributes
"""
domain = self._get_domain(identity, readonly=True)
tree = ET.fromstring(domain.XMLDesc())
tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE))
simple_storage = defaultdict(lambda: defaultdict(DeviceList=list()))
for disk_element in tree.findall(".//disk/target[@bus]/.."):

View File

@ -165,7 +165,7 @@ class LibvirtDriverTestCase(base.BaseTestCase):
domain_mock.injectNMI.assert_called_once_with()
@mock.patch('libvirt.open', autospec=True)
def test_power_cycle_when_off(self, libvirt_mock):
def test_power_cycle(self, libvirt_mock):
with open('sushy_tools/tests/unit/emulator/'
'domain_boot_os.xml', 'r') as f:
data = f.read()
@ -181,29 +181,9 @@ class LibvirtDriverTestCase(base.BaseTestCase):
gps_mock.return_value = 'Off'
self.test_driver.set_boot_device(self.uuid, 'Cd')
self.assertTrue(gps_mock.called)
self.assertFalse(gps_mock.called)
self.assertFalse(sps_mock.called)
@mock.patch('libvirt.open', autospec=True)
def test_power_cycle_when_on(self, libvirt_mock):
with open('sushy_tools/tests/unit/emulator/'
'domain_boot_os.xml', 'r') as f:
data = f.read()
conn_mock = libvirt_mock.return_value
domain_mock = conn_mock.lookupByUUID.return_value
domain_mock.XMLDesc.return_value = data
with mock.patch.object(
self.test_driver, 'get_power_state') as gps_mock:
with mock.patch.object(
self.test_driver, 'set_power_state') as sps_mock:
gps_mock.return_value = 'On'
self.test_driver.set_boot_device(self.uuid, 'Cd')
self.assertTrue(gps_mock.called)
self.assertTrue(sps_mock.called)
@mock.patch('libvirt.openReadOnly', autospec=True)
def test_get_boot_device_os(self, libvirt_mock):
with open('sushy_tools/tests/unit/emulator/'