Merge "Save previous hd boot device when setting Pxe"

This commit is contained in:
Zuul 2023-02-17 17:45:33 +00:00 committed by Gerrit Code Review
commit 7115db2f3a
3 changed files with 119 additions and 2 deletions

View File

@ -412,9 +412,11 @@ class LibvirtDriver(AbstractSystemsDriver):
tree = ET.fromstring(self.get_xml_desc(domain))
# Remove bootloader configuration
os_element_order = []
for os_element in tree.findall('os'):
for boot_element in os_element.findall('boot'):
os_element_order.append(boot_element.get('dev'))
os_element.remove(boot_element)
if self.SUSHY_EMULATOR_IGNORE_BOOT_DEVICE:
@ -437,20 +439,31 @@ class LibvirtDriver(AbstractSystemsDriver):
raise error.FishyError(msg)
target_device_elements = []
cur_hd_osboot_elements = []
cur_hd_order_elements = []
# Remove per-disk boot configuration
# We should save at least hdd boot entries instead of just removing
# everything. In some scenarious PXE after provisioning stops replying
# and if there is no other boot device, then vm will fail to boot
# cdrom and floppy are ignored.
for disk_element in devices_element.findall('disk'):
device_attr = disk_element.get('device')
if device_attr is None:
continue
boot_elements = disk_element.findall('boot')
# NOTE(etingof): multiple devices of the same type not supported
if device_attr == target:
target_device_elements.append(disk_element)
elif 'hd' in os_element_order:
cur_hd_osboot_elements.append(disk_element)
elif boot_elements:
cur_hd_order_elements.append(disk_element)
for boot_element in disk_element.findall('boot'):
for boot_element in boot_elements:
disk_element.remove(boot_element)
target = self.INTERFACE_MAP.get(boot_source)
@ -472,6 +485,15 @@ class LibvirtDriver(AbstractSystemsDriver):
raise error.FishyError(msg)
# OS boot and per device boot order are mutually exclusive
if cur_hd_osboot_elements:
sorted_hd_elements = sorted(
cur_hd_osboot_elements,
key=lambda child: child.find('target').get('dev'))
target_device_elements.extend(sorted_hd_elements)
else:
target_device_elements.extend(cur_hd_order_elements)
# NOTE(etingof): Make all chosen devices bootable (important for NICs)
for order, target_device_element in enumerate(target_device_elements):
@ -774,7 +796,10 @@ class LibvirtDriver(AbstractSystemsDriver):
def _process_bios(self, identity,
bios_attributes=DEFAULT_BIOS_ATTRIBUTES,
update_existing_attributes=False):
"""Process Libvirt domain XML for BIOS attributes and update it if necessary
"""Process Libvirt domain XML for BIOS attributes
Process Libvirt domain XML for BIOS attributes and update it if
necessary
:param identity: libvirt domain name or ID
:param bios_attributes: Full list of BIOS attributes to use if
@ -785,6 +810,7 @@ class LibvirtDriver(AbstractSystemsDriver):
:raises: `error.FishyError` if BIOS attributes cannot be saved
"""
domain = self._get_domain(identity)
result = self._process_bios_attributes(

View File

@ -0,0 +1,31 @@
<domain type='qemu'>
<name>QEmu-fedora-i686</name>
<uuid>c7a5fdbd-cdaf-9455-926a-d65c16db1809</uuid>
<memory>219200</memory>
<currentMemory>219200</currentMemory>
<vcpu>2</vcpu>
<os>
<type arch='x86_64' machine='pc'>hvm</type>
<boot dev='hd'/>
<loader type='rom'/>
</os>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='file' device='cdrom'>
<source file='/home/user/boot.iso'/>
<target dev='hdc'/>
<readonly/>
</disk>
<disk type='file' device='disk'>
<source file='/home/user/fedora.img'/>
<target dev='hda'/>
</disk>
<interface type='network'>
<source network='default'/>
<mac address='52:54:00:da:ac:54'/>
<model type='virtio'/>
<address type='pci' domain='0x0000' bus='0x01' slot='0x01' function='0x0'/>
</interface>
<graphics type='vnc' port='-1'/>
</devices>
</domain>

View File

@ -327,6 +327,66 @@ class LibvirtDriverTestCase(base.BaseTestCase):
self.assertIn(expected, conn_mock.defineXML.call_args[0][0])
@mock.patch('libvirt.open', autospec=True)
def test_set_boot_device_network_from_hd(self, libvirt_mock):
with open('sushy_tools/tests/unit/emulator/'
'domain_to_boot_pxe.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', return_value='Off'):
self.test_driver.set_boot_device(self.uuid, 'Pxe')
conn_mock.defineXML.assert_called_once_with(mock.ANY)
newtree = ET.fromstring(conn_mock.defineXML.call_args[0][0])
# check that os section does not have boot defined.
# We find all os sections if any. Then count about of boot sections.
# And the final summ should be 0
os_boot_amount = sum(
[len(ossec.findall('boot')) for ossec in newtree.findall('os')])
self.assertEqual(0, os_boot_amount)
# check that Network device has order=1
interface_orders = [
theint.find('boot').get('order')
for theint in newtree.find('devices').findall('interface')]
self.assertIn('1', interface_orders)
# Check that we have at least one hd device set after a network device
diskdrives_order_sum = len([
thedrive.find('boot').get('order')
for thedrive in newtree.find('devices').findall('disk')])
self.assertEqual(2, diskdrives_order_sum)
# Check overal config to match expected fixture
expected = '<domain type="qemu">\n <name>QEmu-fedora-i686</name>\n '\
' <uuid>c7a5fdbd-cdaf-9455-926a-d65c16db1809</uuid>\n '\
'<memory>219200</memory>\n '\
'<currentMemory>219200</currentMemory>\n <vcpu>2</vcpu>\n '\
'<os>\n <type arch="x86_64" machine="pc">hvm</type>\n '\
'<loader type="rom" />\n </os>\n <devices>\n '\
'<emulator>/usr/bin/qemu-system-x86_64</emulator>\n '\
'<disk type="file" device="cdrom">\n '\
'<source file="/home/user/boot.iso" />\n '\
'<target dev="hdc" />\n <readonly />\n '\
'<boot order="3" /></disk>\n '\
'<disk type="file" device="disk">\n '\
'<source file="/home/user/fedora.img" />\n '\
'<target dev="hda" />\n <boot order="2" /></disk>\n '\
'<interface type="network">\n '\
'<source network="default" />\n '\
'<mac address="52:54:00:da:ac:54" />\n '\
'<model type="virtio" />\n <address type="pci" '\
'domain="0x0000" bus="0x01" slot="0x01" function="0x0" />\n '\
'<boot order="1" /></interface>\n '\
'<graphics type="vnc" port="-1" />\n </devices>\n</domain>'
self.assertIn(expected, conn_mock.defineXML.call_args[0][0])
@mock.patch('libvirt.openReadOnly', autospec=True)
def test_get_boot_mode_legacy(self, libvirt_mock):
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f: