diff --git a/releasenotes/notes/virtual-media-alternate-bus-6d984ae45acb7ac4.yaml b/releasenotes/notes/virtual-media-alternate-bus-6d984ae45acb7ac4.yaml
new file mode 100644
index 00000000..0512ca59
--- /dev/null
+++ b/releasenotes/notes/virtual-media-alternate-bus-6d984ae45acb7ac4.yaml
@@ -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.
diff --git a/sushy_tools/emulator/resources/systems/libvirtdriver.py b/sushy_tools/emulator/resources/systems/libvirtdriver.py
index 36016c39..e7c3a3e5 100644
--- a/sushy_tools/emulator/resources/systems/libvirtdriver.py
+++ b/sushy_tools/emulator/resources/systems/libvirtdriver.py
@@ -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 '
diff --git a/sushy_tools/tests/unit/emulator/domain-sata.xml b/sushy_tools/tests/unit/emulator/domain-sata.xml
new file mode 100644
index 00000000..6920bb67
--- /dev/null
+++ b/sushy_tools/tests/unit/emulator/domain-sata.xml
@@ -0,0 +1,24 @@
+
+ QEmu-fedora-i686
+ c7a5fdbd-cdaf-9455-926a-d65c16db1809
+ 219200
+ 219200
+ 2
+
+ hvm
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sushy_tools/tests/unit/emulator/domain-scsi.xml b/sushy_tools/tests/unit/emulator/domain-scsi.xml
new file mode 100644
index 00000000..74fa61a3
--- /dev/null
+++ b/sushy_tools/tests/unit/emulator/domain-scsi.xml
@@ -0,0 +1,24 @@
+
+ QEmu-fedora-i686
+ c7a5fdbd-cdaf-9455-926a-d65c16db1809
+ 219200
+ 219200
+ 2
+
+ hvm
+
+
+
+
+ /usr/bin/qemu-system-x86_64
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sushy_tools/tests/unit/emulator/resources/systems/test_libvirt.py b/sushy_tools/tests/unit/emulator/resources/systems/test_libvirt.py
index beb94a32..7245aa4e 100644
--- a/sushy_tools/tests/unit/emulator/resources/systems/test_libvirt.py
+++ b/sushy_tools/tests/unit/emulator/resources/systems/test_libvirt.py
@@ -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 = (''
+ ''
+ '')
+ 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 = (''
+ ''
+ '')
+ 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 = (''
+ ''
+ '')
+ 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)