Fix libvirt CD image insertion

Sometimes libvirt refuses to take our freshly burnt ISO image
and put it into /dev/cdrom. The error is:

  Found duplicate drive address for disk with target name 'vda'
  controller='0' bus='0' target='0' unit='1'

The problem seems to be that despite libvirt docs spare the user
from assigning `<address>` to device, sometimes libvirt fails to
find a free spot on the bus.

This patch attempts to enumerate already existing devices on the
bus and pick a free `unit`. Then we will craft a seemingly proper
address and add it to `disk` libvirt configuration.

Change-Id: I5975e0cd016ede7a5685eaca34c19af295f7f172
This commit is contained in:
Ilya Etingof 2019-11-08 17:42:34 +01:00
parent 4279ee0622
commit 723ffd231f
1 changed files with 53 additions and 16 deletions

View File

@ -863,18 +863,57 @@ class LibvirtDriver(AbstractSystemsDriver):
raise error.FishyError(
'Unknown device %s at %s' % (device, identity))
tgt_dev, tgt_bus = self.DEVICE_TARGET_MAP[device]
# Enumerate existing disks to find a free unit on the bus
free_units = {i for i in range(100)}
disk_elements = device_element.findall('disk')
for disk_element in disk_elements:
target_element = disk_element.find('target')
if target_element is None:
continue
bus_type = target_element.attrib.get('bus')
if bus_type != tgt_bus:
continue
address_element = disk_element.find('address')
if address_element is None:
continue
unit_num = address_element.attrib.get('unit')
if unit_num is None:
continue
if unit_num in free_units:
free_units.remove(unit_num)
if not free_units:
msg = ('No free %(bus)s bus unit found in the libvirt domain '
'"%(identity)s" configuration' % {'identity': identity,
'bus': tgt_bus})
raise error.FishyError(msg)
# Add disk element pointing to the boot image
disk_element = ET.SubElement(device_element, 'disk')
disk_element.set('type', 'file')
disk_element.set('device', lv_device)
tgt_dev, tgt_bus = self.DEVICE_TARGET_MAP[device]
target_element = ET.SubElement(disk_element, 'target')
target_element.set('dev', tgt_dev)
target_element.set('bus', tgt_bus)
address_element = ET.SubElement(disk_element, 'address')
address_element.set('type', 'drive')
address_element.set('controller', '0')
address_element.set('bus', '0')
address_element.set('target', '0')
address_element.set('unit', '%s' % min(free_units))
driver_element = ET.SubElement(disk_element, 'driver')
driver_element.set('name', 'qemu')
driver_element.set('type', 'raw')
@ -889,12 +928,6 @@ class LibvirtDriver(AbstractSystemsDriver):
identity = domain.UUIDString()
device_element = domain_tree.find('devices')
if device_element is None:
msg = ('Missing "devices" tag in the libvirt domain '
'"%(identity)s" configuration' % {'identity': identity})
raise error.FishyError(msg)
try:
lv_device = self.BOOT_DEVICE_MAP[device]
@ -908,7 +941,7 @@ class LibvirtDriver(AbstractSystemsDriver):
'"%(identity)s" configuration' % {'identity': identity})
raise error.FishyError(msg)
# First, remove all existing devices
# Remove all existing devices
disk_elements = device_element.findall('disk')
for disk_element in disk_elements:
@ -937,19 +970,23 @@ class LibvirtDriver(AbstractSystemsDriver):
if boot_image:
try:
self._add_boot_image(domain, domain_tree, device,
boot_image, write_protected)
self._add_boot_image(domain, domain_tree, device,
boot_image, write_protected)
with libvirt_open(self._uri) as conn:
xml = ET.tostring(domain_tree)
try:
conn.defineXML(xml.decode('utf-8'))
except Exception as e:
self._logger.error('Rejected libvirt domain XML is %s', xml)
except libvirt.libvirtError as e:
msg = ('Error changing boot image at libvirt URI "%(uri)s": '
'%(error)s' % {'uri': self._uri, 'error': e})
raise error.FishyError(msg)
with libvirt_open(self._uri) as conn:
conn.defineXML(ET.tostring(domain_tree).decode('utf-8'))
def _find_device_by_path(self, vol_path):
"""Get device attributes using path