Save previous hd boot device when setting Pxe
Assuming that sushy_tool ignores "BootSourceOverrideEnabled": "Once" for libvirt backend, we define a new boot device permanently. And if the pxe server does not reply after the first successful provision, we just get a vm which doesn't manage to boot after provision because netboot is the only option and no reply from pxe. This patch adds two additional lists of previously configured boot hard drives for the vm at the end of the boot order. The first list is for the os section If it is defined, it just adds all hard drives to the list. The second list is for the actual drive section with the boot directive. Tried to save script logic as much as possible. But it might be reasonable to pass one additional argument to the set_boot_device function to do such altering when "BootSourceOverrideEnabled" is defined and has "Once" as a value Added testcase to verify boot order definition Change-Id: I0abcab9ccbffcc9b06382b0e6d335edd20db25bf
This commit is contained in:
parent
8427349985
commit
2a628d4099
|
@ -403,9 +403,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:
|
||||
|
@ -428,20 +430,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)
|
||||
|
@ -463,6 +476,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):
|
||||
|
@ -701,7 +723,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
|
||||
|
@ -712,6 +737,7 @@ class LibvirtDriver(AbstractSystemsDriver):
|
|||
|
||||
:raises: `error.FishyError` if BIOS attributes cannot be saved
|
||||
"""
|
||||
|
||||
domain = self._get_domain(identity)
|
||||
|
||||
result = self._process_bios_attributes(
|
||||
|
|
|
@ -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>
|
|
@ -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(self, libvirt_mock):
|
||||
with open('sushy_tools/tests/unit/emulator/domain.xml', 'r') as f:
|
||||
|
|
Loading…
Reference in New Issue