Support non-ide virtual media buses

Also fix a minor possible bug with interpretation of XML returning
a string trying to be compared to a number, and adds two test files
and associated tests for the injection of SATA or SCSI removable
disk drives.

Story: 2007881
Task: 40249

Change-Id: Ia926a8f9d8debaff9763aeebcd986f9056cca700
This commit is contained in:
Julia Kreger 2020-07-01 09:01:12 -07:00
parent 74769fac82
commit ff2be838b9
5 changed files with 168 additions and 6 deletions

View File

@ -0,0 +1,5 @@
---
features:
- |
Adds basic support for virtual media devices on non-IDE buses, such as SATA and
SCSI, as IDE devices are not supported on Q35 libvirt domains.

View File

@ -118,10 +118,10 @@ class LibvirtDriver(AbstractSystemsDriver):
DEVICE_TYPE_MAP_REV = {v: k for k, v in DEVICE_TYPE_MAP.items()}
# target device, controller ID
# target device, controller ID for libvirt domain
DEVICE_TARGET_MAP = {
constants.DEVICE_TYPE_FLOPPY: ('fda', 'fdc'),
constants.DEVICE_TYPE_CD: ('hdc', 'ide')
constants.DEVICE_TYPE_CD: ('hdc', 'ide'),
}
DEFAULT_BIOS_ATTRIBUTES = {"BootMode": "Uefi",
@ -864,7 +864,21 @@ class LibvirtDriver(AbstractSystemsDriver):
raise error.FishyError(
'Unknown device %s at %s' % (device, identity))
tgt_dev, tgt_bus = self.DEVICE_TARGET_MAP[device]
disk_elements = device_element.findall('disk')
controller_type = 'ide'
for disk_element in disk_elements:
target_element = disk_element.find('target')
if target_element is None:
continue
elif target_element.attrib.get('bus') == 'scsi':
controller_type = 'scsi'
elif target_element.attrib.get('bus') == 'sata':
controller_type = 'sata'
if controller_type == 'ide':
tgt_dev, tgt_bus = self.DEVICE_TARGET_MAP[device]
else:
tgt_dev, tgt_bus = ('sdc', controller_type)
# Enumerate existing disks to find a free unit on the bus
@ -889,8 +903,8 @@ class LibvirtDriver(AbstractSystemsDriver):
if unit_num is None:
continue
if unit_num in free_units:
free_units.remove(unit_num)
if int(unit_num) in free_units:
free_units.remove(int(unit_num))
if not free_units:
msg = ('No free %(bus)s bus unit found in the libvirt domain '

View File

@ -0,0 +1,24 @@
<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='cdrom'/>
<loader type='rom'/>
</os>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='file' device='disk'>
<source file='/home/user/fedora.img'/>
<target bus='sata' dev='sda'/>
<address bus='0' target='0' type='drive' unit='0'/>
</disk>
<interface type='network'>
<source network='default'/>
</interface>
<graphics type='vnc' port='-1'/>
</devices>
</domain>

View File

@ -0,0 +1,24 @@
<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='cdrom'/>
<loader type='rom'/>
</os>
<devices>
<emulator>/usr/bin/qemu-system-x86_64</emulator>
<disk type='file' device='disk'>
<source file='/home/user/fedora.img'/>
<target bus='scsi' dev='sda'/>
<address bus='0' target='0' type='drive' unit='0'/>
</disk>
<interface type='network'>
<source network='default'/>
</interface>
<graphics type='vnc' port='-1'/>
</devices>
</domain>

View File

@ -525,7 +525,102 @@ class LibvirtDriverTestCase(base.BaseTestCase):
volume_mock = pool_mock.createXML.return_value
volume_mock.upload.assert_called_once_with(mock.ANY, 0, mock.ANY)
conn_mock.defineXML.assert_called_once_with(mock.ANY)
expected_disk = ('<disk device="cdrom" type="file">'
'<target bus="ide" dev="hdc" />'
'<address bus="0" controller="0" '
'target="0" type="drive" unit="0" />')
self.assertEqual(1, conn_mock.defineXML.call_count)
self.assertIn(expected_disk, conn_mock.defineXML.call_args[0][0])
@mock.patch('sushy_tools.emulator.resources.systems.libvirtdriver'
'.os.stat', autospec=True)
@mock.patch('sushy_tools.emulator.resources.systems.libvirtdriver'
'.open')
@mock.patch('libvirt.open', autospec=True)
@mock.patch('libvirt.openReadOnly', autospec=True)
def test_set_boot_image_sata(self, libvirt_mock, libvirt_rw_mock,
open_mock, stat_mock):
with open('sushy_tools/tests/unit/emulator/domain-sata.xml', 'r') as f:
data = f.read()
conn_mock = libvirt_rw_mock.return_value
domain_mock = conn_mock.lookupByUUID.return_value
domain_mock.XMLDesc.return_value = data
pool_mock = conn_mock.storagePoolLookupByName.return_value
with open('sushy_tools/tests/unit/emulator/pool.xml', 'r') as f:
data = f.read()
pool_mock.XMLDesc.return_value = data
with mock.patch.object(
self.test_driver, 'get_power_state', return_value='Off'):
with mock.patch.object(
self.test_driver, 'get_boot_device', return_value=None):
self.test_driver.set_boot_image(
self.uuid, 'Cd', '/tmp/image.iso')
conn_mock = libvirt_rw_mock.return_value
pool_mock.listAllVolumes.assert_called_once_with()
stat_mock.assert_called_once_with('/tmp/image.iso')
pool_mock.createXML.assert_called_once_with(mock.ANY)
volume_mock = pool_mock.createXML.return_value
volume_mock.upload.assert_called_once_with(mock.ANY, 0, mock.ANY)
expected_disk = ('<disk device="cdrom" type="file">'
'<target bus="sata" dev="sdc" />'
'<address bus="0" controller="0" '
'target="0" type="drive" unit="1" />')
self.assertEqual(1, conn_mock.defineXML.call_count)
self.assertIn(expected_disk, conn_mock.defineXML.call_args[0][0])
@mock.patch('sushy_tools.emulator.resources.systems.libvirtdriver'
'.os.stat', autospec=True)
@mock.patch('sushy_tools.emulator.resources.systems.libvirtdriver'
'.open')
@mock.patch('libvirt.open', autospec=True)
@mock.patch('libvirt.openReadOnly', autospec=True)
def test_set_boot_image_scsi(self, libvirt_mock, libvirt_rw_mock,
open_mock, stat_mock):
with open('sushy_tools/tests/unit/emulator/domain-scsi.xml', 'r') as f:
data = f.read()
conn_mock = libvirt_rw_mock.return_value
domain_mock = conn_mock.lookupByUUID.return_value
domain_mock.XMLDesc.return_value = data
pool_mock = conn_mock.storagePoolLookupByName.return_value
with open('sushy_tools/tests/unit/emulator/pool.xml', 'r') as f:
data = f.read()
pool_mock.XMLDesc.return_value = data
with mock.patch.object(
self.test_driver, 'get_power_state', return_value='Off'):
with mock.patch.object(
self.test_driver, 'get_boot_device', return_value=None):
self.test_driver.set_boot_image(
self.uuid, 'Cd', '/tmp/image.iso')
conn_mock = libvirt_rw_mock.return_value
pool_mock.listAllVolumes.assert_called_once_with()
stat_mock.assert_called_once_with('/tmp/image.iso')
pool_mock.createXML.assert_called_once_with(mock.ANY)
volume_mock = pool_mock.createXML.return_value
volume_mock.upload.assert_called_once_with(mock.ANY, 0, mock.ANY)
expected_disk = ('<disk device="cdrom" type="file">'
'<target bus="scsi" dev="sdc" />'
'<address bus="0" controller="0" '
'target="0" type="drive" unit="1" />')
self.assertEqual(1, conn_mock.defineXML.call_count)
self.assertIn(expected_disk, conn_mock.defineXML.call_args[0][0])
@mock.patch('libvirt.open', autospec=True)
@mock.patch('libvirt.openReadOnly', autospec=True)