Beispiel #1
0
    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)
Beispiel #2
0
    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
Beispiel #3
0
    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)
Beispiel #4
0
    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)
Beispiel #5
0
    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)
Beispiel #6
0
    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)
Beispiel #7
0
    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
Beispiel #8
0
    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))
Beispiel #9
0
    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)
Beispiel #10
0
    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)
Beispiel #11
0
    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
Beispiel #12
0
    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')
Beispiel #13
0
    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})
Beispiel #14
0
    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)
Beispiel #15
0
    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
Beispiel #16
0
    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())
Beispiel #17
0
    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)
Beispiel #18
0
    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)
Beispiel #19
0
    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
Beispiel #20
0
 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')
Beispiel #21
0
 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')
Beispiel #22
0
    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')
Beispiel #23
0
    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
Beispiel #24
0
    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)
Beispiel #25
0
    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)