def add_certificate(self, identity, device, cert_string, cert_type): device_info = self._get_device(identity, device) if "Certificate" in device_info: raise error.FishyError("Virtual media certificate already exists", code=409) device_info["Certificate"] = {'Type': cert_type, 'String': cert_string} self._devices[(identity, device)] = device_info return Certificate(_CERT_ID, cert_string, cert_type)
def set_indicator_state(self, identity, state): """Set indicator state :param identity: indicator identity :param state: indicator state as one of *Lit*, *Blinking*, *Off* :raises: `error.FishyError` if desired state can't be set """ if state not in ('Lit', 'Off', 'Blinking'): raise error.FishyError( 'Unknown indicator state %s, ID %s' % (state, identity)) self._indicators[identity] = state
def get_drives(self, identity, storage_id): try: uu_identity = str(uuid.UUID(identity)) return self._drives[(uu_identity, storage_id)] except (ValueError, KeyError): msg = ('Error finding drive for System UUID "%s" and Storage ID ' '"%s"', identity, storage_id) self._logger.debug(msg) raise error.FishyError(msg)
def get_storage_col(self, identity): try: uu_identity = str(uuid.UUID(identity)) return self._storage[uu_identity] except KeyError: msg = ('Error finding storage collection by UUID ' '"%(identity)s"' % {'identity': identity}) self._logger.debug(msg) raise error.FishyError(msg)
def set_boot_image(self, identity, device, boot_image=None, write_protected=True): """Set backend VM boot image :param identity: libvirt domain name or ID :param device: device type (from `sushy_tools.emulator.constants`) :param boot_image: path to the image file or `None` to remove configured image entirely :param write_protected: expose media as read-only or writable :raises: `error.FishyError` if boot device can't be set """ domain = self._get_domain(identity) domain_tree = ET.fromstring( domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE)) self._remove_boot_images(domain, domain_tree, device) boot_device = None if boot_image: self._add_boot_image(domain, domain_tree, device, boot_image, write_protected) boot_device = self.get_boot_device(identity) 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) msg = ('Error changing boot image at libvirt URI "%(uri)s": ' '%(error)s' % { 'uri': self._uri, 'error': e }) raise error.FishyError(msg) if device == boot_device: self.set_boot_device(identity, boot_device)
def __enter__(self): try: self._conn = (libvirt.openReadOnly(self._uri) if self._readonly else libvirt.open(self._uri)) return self._conn except libvirt.libvirtError as e: msg = ('Error when connecting to the libvirt URI "%(uri)s": ' '%(error)s' % { 'uri': self._uri, 'error': e }) raise error.FishyError(msg)
def _upload_image(self, domain, conn, boot_image): pool = conn.storagePoolLookupByName(self.STORAGE_POOL) pool_tree = ET.fromstring(pool.XMLDesc()) # Find out path to images pool_path_element = pool_tree.find('target/path') if pool_path_element is None: msg = ('Missing "target/path" tag in the libvirt ' 'storage pool "%(pool)s"' '' % { 'pool': self.STORAGE_POOL }) raise error.FishyError(msg) image_name = '%s-%s.img' % (os.path.basename(boot_image).replace( '.', '-'), domain.UUIDString()) image_path = os.path.join(pool_path_element.text, image_name) image_size = os.stat(boot_image).st_size # Remove already existing volume volumes_names = [v.name() for v in pool.listAllVolumes()] if image_name in volumes_names: volume = pool.storageVolLookupByName(image_name) volume.delete() # Create new volume volume = pool.createXML(self.STORAGE_VOLUME_XML % { 'name': image_name, 'path': image_path, 'size': image_size }) # Upload image to hypervisor stream = conn.newStream() volume.upload(stream, 0, image_size) def read_file(stream, nbytes, fl): return fl.read(nbytes) stream.sendAll(read_file, open(boot_image, 'rb')) stream.finish() return image_path
def _get_device(self, identity, device): try: return self._devices[(identity, device)] except KeyError: self._devices.update({(identity, k): v for k, v in self._device_types.items()}) try: return self._devices[(identity, device)] except KeyError: raise error.FishyError( 'No such virtual media device %s owned by resource ' '%s' % (device, identity))
def _get_instance(self, identity): instance = self._cc.get_server(identity) if instance: if identity != instance.id: raise error.AliasAccessError(instance.id) return instance msg = ('Error finding instance by UUID "%(identity)s" at OS ' 'cloud %(os_cloud)s"' % {'identity': identity, 'os_cloud': self._os_cloud}) logger.debug(msg) raise error.FishyError(msg)
def set_boot_mode(self, identity, boot_mode): """Set computer system boot mode. :param boot_mode: optional string literal requesting boot mode change on the system. If not specified, current boot mode is returned. Valid values are: *Uefi*, *Legacy*. :raises: `error.FishyError` if boot mode can't be set """ # just to make sure passed identity exists self._get_instance(identity) msg = ('The cloud driver %(driver)s does not allow changing boot ' 'mode through Redfish' % {'driver': self.driver}) raise error.FishyError(msg)
def get_boot_image(self, identity, device): """Get backend VM boot image info :param identity: libvirt domain name or ID :param device: device type (from `sushy_tools.emulator.constants`) :returns: a `tuple` of (boot_image, write_protected, inserted) :raises: `error.FishyError` if boot device can't be accessed """ domain = self._get_domain(identity, readonly=True) tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE)) device_element = 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) for disk_element in device_element.findall('disk'): dev_type = disk_element.attrib.get('device') if (dev_type not in self.DEVICE_TYPE_MAP_REV or dev_type != self.DEVICE_TYPE_MAP.get(device)): continue source_element = disk_element.find('source') if source_element is None: continue boot_image = source_element.attrib.get('file') if boot_image is None: continue read_only = disk_element.find('readonly') or False inserted = ( self.get_boot_device(identity) == constants.DEVICE_TYPE_CD) if inserted: inserted = self.get_boot_mode(identity) == 'UEFI' return boot_image, read_only, inserted return '', False, False
def __init__(self): chassis = self._config.get('SUSHY_EMULATOR_CHASSIS') if not chassis: # Default chassis chassis = [{ u'Id': u'SheetMetalChassis', u'Name': u'Chassis', u'UUID': u'15693887-7984-9484-3272-842188918912', }] self._chassis_by_id = {x['Id']: x for x in chassis} self._chassis_by_uuid = {x['UUID']: x for x in chassis if 'UUID' in x} self._chassis_by_name = {x['Name']: x for x in chassis if 'Name' in x} if len(self._chassis_by_uuid) != len(chassis): raise error.FishyError( 'Conflicting UUIDs in static chassis configuration')
def set_power_state(self, identity, state): """Set computer system power state :param identity: OpenStack instance name or ID :param state: optional string literal requesting power state transition If not specified, current system power state is returned. Valid values are: *On*, *ForceOn*, *ForceOff*, *GracefulShutdown*, *GracefulRestart*, *ForceRestart*, *Nmi*. :raises: `error.FishyError` if power state can't be set """ instance = self._get_instance(identity) if state in ('On', 'ForceOn'): if instance.power_state != self.NOVA_POWER_STATE_ON: self._cc.compute.start_server(instance.id) elif state == 'ForceOff': if instance.power_state == self.NOVA_POWER_STATE_ON: self._cc.compute.stop_server(instance.id) elif state == 'GracefulShutdown': if instance.power_state == self.NOVA_POWER_STATE_ON: self._cc.compute.stop_server(instance.id) elif state == 'GracefulRestart': if instance.power_state == self.NOVA_POWER_STATE_ON: self._cc.compute.reboot_server( instance.id, reboot_type='SOFT' ) elif state == 'ForceRestart': if instance.power_state == self.NOVA_POWER_STATE_ON: self._cc.compute.reboot_server( instance.id, reboot_type='HARD' ) # NOTE(etingof) can't support `state == "Nmi"` as # openstacksdk does not seem to support that else: raise error.FishyError( 'Unknown ResetType "%(state)s"' % {'state': state})
def set_power_state(self, identity, state): """Set computer system power state :param identity: libvirt domain name or ID :param state: string literal requesting power state transition. If not specified, current system power state is returned. Valid values are: *On*, *ForceOn*, *ForceOff*, *GracefulShutdown*, *GracefulRestart*, *ForceRestart*, *Nmi*. :raises: `error.FishyError` if power state can't be set """ domain = self._get_domain(identity) try: if state in ('On', 'ForceOn'): if not domain.isActive(): domain.create() elif state == 'ForceOff': if domain.isActive(): domain.destroy() elif state == 'GracefulShutdown': if domain.isActive(): domain.shutdown() elif state == 'GracefulRestart': if domain.isActive(): domain.reboot() elif state == 'ForceRestart': if domain.isActive(): domain.reset() elif state == 'Nmi': if domain.isActive(): domain.injectNMI() except libvirt.libvirtError as e: msg = ('Error changing power state at libvirt URI "%(uri)s": ' '%(error)s' % { 'uri': self._uri, 'error': e }) raise error.FishyError(msg)
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 :param identity: libvirt domain name or ID :param bios_attributes: Full list of BIOS attributes to use if they are missing or update necessary :param update_existing_attributes: Update existing BIOS attributes :returns: New or existing dict of BIOS attributes :raises: `error.FishyError` if BIOS attributes cannot be saved """ domain = self._get_domain(identity) result = self._process_bios_attributes( domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE), bios_attributes, update_existing_attributes) if result.attributes_written: try: with libvirt_open(self._uri) as conn: conn.defineXML(ET.tostring(result.tree).decode('utf-8')) except libvirt.libvirtError as e: msg = ('Error updating BIOS attributes' ' at libvirt URI "%(uri)s": ' '%(error)s' % { 'uri': self._uri, 'error': e }) raise error.FishyError(msg) return result.bios_attributes
def _get_domain(self, identity, readonly=False): with libvirt_open(self._uri, readonly=readonly) as conn: try: uu_identity = uuid.UUID(identity) return conn.lookupByUUID(uu_identity.bytes) except (ValueError, libvirt.libvirtError): try: domain = conn.lookupByName(identity) except libvirt.libvirtError as ex: msg = ('Error finding domain by name/UUID "%(identity)s" ' 'at libvirt URI %(uri)s": %(err)s' % { 'identity': identity, 'uri': self._uri, 'err': ex }) self._logger.debug(msg) raise error.FishyError(msg) raise error.AliasAccessError(domain.UUIDString())
def _get_chassis(self, identity): try: uu_identity = str(uuid.UUID(identity)) return self._chassis_by_uuid[uu_identity] except (ValueError, KeyError): try: uu_identity = self._chassis_by_name[identity]['UUID'] except KeyError: try: uu_identity = self._chassis_by_id[identity]['UUID'] except KeyError: msg = ('Error finding chassis by UUID/Name/Id ' '"%(identity)s"' % {'identity': identity}) self._logger.debug(msg) raise error.FishyError(msg) raise error.AliasAccessError(uu_identity)
def set_boot_mode(self, identity, boot_mode): """Set computer system boot mode. :param identity: libvirt domain name or ID :param boot_mode: string literal requesting boot mode change on the system. Valid values are: *UEFI*, *Legacy*. :raises: `error.FishyError` if boot mode can't be set """ domain = self._get_domain(identity, readonly=True) # XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE)) try: loader_type = self.BOOT_MODE_MAP[boot_mode] except KeyError: msg = ('Unknown boot mode requested: ' '%(boot_mode)s' % { 'boot_mode': boot_mode }) raise error.BadRequest(msg) os_elements = tree.findall('os') if len(os_elements) != 1: msg = ('Can\'t set boot mode because "os" element must be present ' 'exactly once in domain "%(identity)s" ' 'configuration' % { 'identity': identity }) raise error.FishyError(msg) os_element = os_elements[0] type_element = os_element.find('type') if type_element is None: os_arch = None else: os_arch = type_element.get('arch') try: loader_path = self.BOOT_LOADER_MAP[boot_mode][os_arch] except KeyError: self._logger.warning( 'Boot loader binary is not configured for ' 'boot mode %s and OS architecture %s. ' 'Assuming default boot loader for the domain.', boot_mode, os_arch) loader_path = None loader_elements = os_element.findall('loader') if len(loader_elements) > 1: msg = ('Can\'t set boot mode because "loader" element must be ' 'present exactly once in domain "%(identity)s" ' 'configuration' % { 'identity': identity }) raise error.FishyError(msg) if loader_elements: loader_element = loader_elements[0] if loader_element.text not in self.KNOWN_BOOT_LOADERS: msg = ('Unknown boot loader path "%(path)s" in domain ' '"%(identity)s" configuration encountered while ' 'setting boot mode "%(mode)s", system architecture ' '"%(arch)s". Consider adding this loader path to ' 'emulator config.' % { 'identity': identity, 'mode': boot_mode, 'arch': os_arch, 'path': loader_element.text }) raise error.FishyError(msg) if loader_path: loader_element.set('type', loader_type) loader_element.set('readonly', 'yes') loader_element.text = loader_path else: # NOTE(etingof): path must be present or element must be absent os_element.remove(loader_element) elif loader_path: loader_element = ET.SubElement(os_element, 'loader') loader_element.set('type', loader_type) loader_element.set('readonly', 'yes') loader_element.text = loader_path with libvirt_open(self._uri) as conn: try: conn.defineXML(ET.tostring(tree).decode('utf-8')) except libvirt.libvirtError as e: msg = ('Error changing boot mode at libvirt URI ' '"%(uri)s": %(error)s' % { 'uri': self._uri, 'error': e }) raise error.FishyError(msg)
def insert_image(self, identity, device, image_url, inserted=True, write_protected=True): """Upload, remove or insert virtual media :param identity: parent resource ID :param device: device name :param image_url: URL to ISO image to place into `device` or `None` to eject currently present media :param inserted: treat currently present media as inserted or not :param write_protected: prevent write access the inserted media :raises: `FishyError` if image can't be manipulated """ device_info = self._get_device(identity, device) try: with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as tmp_file: with requests.get(image_url, stream=True) as rsp: with open(tmp_file.name, 'wb') as fl: for chunk in rsp.iter_content(chunk_size=8192): if chunk: fl.write(chunk) local_file = None content_dsp = rsp.headers.get('content-disposition') if content_dsp: local_file = re.findall('filename="(.+)"', content_dsp) if local_file: local_file = local_file[0] if not local_file: parsed_url = urlparse(image_url) local_file = os.path.basename(parsed_url.path) if not local_file: local_file = 'image.iso' temp_dir = tempfile.mkdtemp( dir=os.path.dirname(tmp_file.name)) local_file_path = os.path.join(temp_dir, local_file) os.rename(tmp_file.name, local_file_path) except Exception as ex: msg = 'Failed fetching image from URL %s: %s' % (image_url, ex) self._logger.error(msg) raise error.FishyError(msg) self._logger.debug('Fetched image %(file)s for %(identity)s' % { 'identity': identity, 'file': local_file }) device_info['Image'] = local_file device_info['Inserted'] = inserted device_info['WriteProtected'] = write_protected device_info['_local_file_path'] = local_file_path self._devices.update({(identity, device): device_info}) return local_file_path
def test_get_manager_not_found(self): self.systems.uuid.side_effect = error.FishyError('boom') self.assertRaises(error.FishyError, self.test_driver.get_manager, 'foo')
def reset_bios(self, identity): """Not supported as Openstack SDK does not expose API for BIOS""" raise error.FishyError( 'Operation not supported by the virtualization driver')
def _add_boot_image(self, domain, domain_tree, device, boot_image, write_protected): 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) controller_type = self._default_controller(domain_tree) with libvirt_open(self._uri) as conn: image_path = self._upload_image(domain, conn, boot_image) try: lv_device = self.BOOT_DEVICE_MAP[device] except KeyError: raise error.BadRequest('Unknown device %s at %s' % (device, identity)) disk_elements = device_element.findall('disk') 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] elif lv_device == 'floppy': tgt_dev, tgt_bus = ('fda', 'fdc') else: tgt_dev, tgt_bus = ('sdx', controller_type) # 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 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 ' '"%(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) 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') source_element = ET.SubElement(disk_element, 'source') source_element.set('file', image_path) if write_protected: ET.SubElement(disk_element, 'readonly')
def insert_image(self, identity, device, image_url, inserted=True, write_protected=True, username=None, password=None): """Upload, remove or insert virtual media :param identity: parent resource ID :param device: device name :param image_url: URL to ISO image to place into `device` or `None` to eject currently present media :param inserted: treat currently present media as inserted or not :param write_protected: prevent write access the inserted media :raises: `FishyError` if image can't be manipulated """ device_info = self._get_device(identity, device) verify_media_cert = device_info.get( 'Verify', # NOTE(dtantsur): it's de facto standard for Redfish to default # to no certificate validation. self._config.get('SUSHY_EMULATOR_VMEDIA_VERIFY_SSL', False)) custom_cert = None if verify_media_cert: try: custom_cert = device_info['Certificate']['String'] except KeyError: self._logger.debug( 'TLS verification is enabled but not custom certificate ' 'is provided, using built-in CA for manager %s, virtual ' 'media device %s', identity, device) else: self._logger.debug( 'Using a custom TLS certificate for manager %s, virtual ' 'media device %s', identity, device) auth = (username, password) if (username and password) else None if custom_cert is not None: custom_cert_file = tempfile.NamedTemporaryFile(mode='wt') custom_cert_file.write(custom_cert) custom_cert_file.flush() verify_media_cert = custom_cert_file.name try: with requests.get(image_url, stream=True, auth=auth, verify=verify_media_cert) as rsp: if rsp.status_code >= 400: self._logger.error( 'Failed fetching image from URL %s: ' 'got HTTP error %s:\n%s', image_url, rsp.status_code, rsp.text) target_code = 502 if rsp.status_code >= 500 else 400 raise error.FishyError( "Cannot download virtual media: got error %s " "from the server" % rsp.status_code, code=target_code) with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as tmp_file: local_file = self._write_from_response( image_url, rsp, tmp_file) temp_dir = tempfile.mkdtemp( dir=os.path.dirname(tmp_file.name)) local_file_path = os.path.join(temp_dir, local_file) os.rename(tmp_file.name, local_file_path) except error.FishyError as ex: msg = 'Failed fetching image from URL %s: %s' % (image_url, ex) self._logger.error(msg) raise # leave the original error intact (code, etc) except Exception as ex: msg = 'Failed fetching image from URL %s: %s' % (image_url, ex) self._logger.exception(msg) raise error.FishyError(msg) finally: if custom_cert is not None: custom_cert_file.close() self._logger.debug('Fetched image %(url)s for %(identity)s' % { 'identity': identity, 'url': image_url }) device_info['Image'] = image_url device_info['ImageName'] = local_file device_info['Inserted'] = inserted device_info['WriteProtected'] = write_protected device_info['UserName'] = username or '' device_info['Password'] = password or '' device_info['_local_file'] = local_file_path self._devices.update({(identity, device): device_info}) return local_file_path
def set_boot_device(self, identity, boot_source): """Get/Set computer system boot device name First remove all boot device configuration from bootloader because that's legacy with libvirt. Then remove possible boot configuration in the per-device settings. Finally, make the desired boot device the only bootable by means of per-device configuration boot option. :param identity: libvirt domain name or ID :param boot_source: string literal requesting boot device change on the system. Valid values are: *Pxe*, *Hdd*, *Cd*. :raises: `error.FishyError` if boot device can't be set """ domain = self._get_domain(identity) # XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS tree = ET.fromstring(domain.XMLDesc(libvirt.VIR_DOMAIN_XML_INACTIVE)) # Remove bootloader configuration for os_element in tree.findall('os'): for boot_element in os_element.findall('boot'): os_element.remove(boot_element) if self.SUSHY_EMULATOR_IGNORE_BOOT_DEVICE: self._logger.warning('Ignoring setting of boot device') boot_element = ET.SubElement(os_element, 'boot') boot_element.set('dev', 'fd') self._defineDomain(tree) return target = self.DISK_DEVICE_MAP.get(boot_source) # Process per-device boot configuration devices_element = tree.find('devices') if devices_element is None: msg = ('Incomplete libvirt domain configuration - <devices> ' 'element is missing in domain ' '%(uuid)s' % { 'uuid': domain.UUIDString() }) raise error.FishyError(msg) target_device_elements = [] # Remove per-disk boot configuration for disk_element in devices_element.findall('disk'): device_attr = disk_element.get('device') if device_attr is None: continue # NOTE(etingof): multiple devices of the same type not supported if device_attr == target: target_device_elements.append(disk_element) for boot_element in disk_element.findall('boot'): disk_element.remove(boot_element) target = self.INTERFACE_MAP.get(boot_source) # Remove per-interface boot configuration for interface_element in devices_element.findall('interface'): if target == 'network': target_device_elements.append(interface_element) for boot_element in interface_element.findall('boot'): interface_element.remove(boot_element) if not target_device_elements: msg = ('Target libvirt device %(target)s does not exist in domain ' '%(uuid)s' % { 'target': boot_source, 'uuid': domain.UUIDString() }) raise error.FishyError(msg) # NOTE(etingof): Make all chosen devices bootable (important for NICs) for order, target_device_element in enumerate(target_device_elements): boot_element = ET.SubElement(target_device_element, 'boot') boot_element.set('order', str(order + 1)) self._defineDomain(tree)
def set_boot_mode(self, identity, boot_mode): """Set computer system boot mode. :param boot_mode: optional string literal requesting boot mode change on the system. If not specified, current boot mode is returned. Valid values are: *Uefi*, *Legacy*. :raises: `error.FishyError` if boot mode can't be set """ domain = self._get_domain(identity, readonly=True) # XML schema: https://libvirt.org/formatdomain.html#elementsOSBIOS tree = ET.fromstring(domain.XMLDesc()) try: loader_type = self.BOOT_MODE_MAP[boot_mode] except KeyError: msg = ('Unknown boot mode requested: ' '%(boot_mode)s' % { 'boot_mode': boot_mode }) raise error.FishyError(msg) for os_element in tree.findall('os'): type_element = os_element.find('type') if type_element is None: os_arch = None else: os_arch = type_element.get('arch') try: loader_path = self.BOOT_LOADER_MAP[boot_mode][os_arch] except KeyError: # NOTE(etingof): assume no specific boot loader loader_path = '' # Update all "loader" elements for loader_element in os_element.findall('loader'): loader_element.set('type', loader_type) # NOTE(etingof): here we override previous boot loader for # for the domain. If it's different than what we have # hardcoded in the BOOT_LOADER_MAP, we won't be able to # revert back to the original boor loader should we change # domain boot mode. loader_element.text = loader_path with libvirt_open(self._uri) as conn: try: conn.defineXML(ET.tostring(tree).decode('utf-8')) except libvirt.libvirtError as e: msg = ('Error changing boot mode at libvirt URI ' '"%(uri)s": %(error)s' % { 'uri': self._uri, 'error': e }) raise error.FishyError(msg)