Ejemplo n.º 1
0
    def _configure_boot_from_volume(self, task):
        """Set information for booting from a remote volume to iRMC.

        :param task: a TaskManager instance containing the node to act on.
        :raises: IRMCOperationError if iRMC operation failed
        """

        irmc_info = irmc_common.parse_driver_info(task.node)
        viom_conf = viom.VIOMConfiguration(irmc_info,
                                           identification=task.node.uuid)

        self._register_lan_ports(viom_conf, task)

        for vt in task.volume_targets:
            if vt.volume_type == 'iscsi':
                self._set_iscsi_target(task, viom_conf, vt)
            elif vt.volume_type == 'fibre_channel':
                self._set_fc_target(task, viom_conf, vt)

        try:
            LOG.debug('Set VIOM configuration for node %(node)s: %(table)s', {
                'node': task.node.uuid,
                'table': viom_conf.dump_json()
            })
            viom_conf.apply()
        except scci.SCCIError as e:
            LOG.error(
                'iRMC failed to set VIOM configuration for node '
                '%(node)s: %(error)s', {
                    'node': task.node.uuid,
                    'error': e
                })
            raise exception.IRMCOperationError(operation='Configure VIOM',
                                               error=e)
Ejemplo n.º 2
0
def _prepare_floppy_image(task, params):
    """Prepares the floppy image for passing the parameters.

    This method prepares a temporary vfat filesystem image, which
    contains the parameters to be passed to the ramdisk.
    Then it uploads the file NFS or CIFS server.

    :param task: a TaskManager instance containing the node to act on.
    :param params: a dictionary containing 'parameter name'->'value' mapping
        to be passed to the deploy ramdisk via the floppy image.
    :returns: floppy image filename
    :raises: ImageCreationFailed, if it failed while creating the floppy image.
    :raises: IRMCOperationError, if copying floppy image file failed.
    """
    floppy_filename = _get_floppy_image_name(task.node)
    floppy_fullpathname = os.path.join(CONF.irmc.remote_image_share_root,
                                       floppy_filename)

    with tempfile.NamedTemporaryFile() as vfat_image_tmpfile_obj:
        images.create_vfat_image(vfat_image_tmpfile_obj.name,
                                 parameters=params)
        try:
            shutil.copyfile(vfat_image_tmpfile_obj.name, floppy_fullpathname)
        except IOError as e:
            operation = _("Copying floppy image file")
            raise exception.IRMCOperationError(operation=operation, error=e)

    return floppy_filename
Ejemplo n.º 3
0
def _attach_virtual_fd(node, floppy_image_filename):
    """Attaches virtual floppy on the node.

    :param node: an ironic node object.
    :raises: IRMCOperationError if insert virtual floppy failed.
    """
    try:
        irmc_client = irmc_common.get_irmc_client(node)

        fd_set_params = scci.get_virtual_fd_set_params_cmd(
            CONF.irmc.remote_image_server, CONF.irmc.remote_image_user_domain,
            scci.get_share_type(CONF.irmc.remote_image_share_type),
            CONF.irmc.remote_image_share_name, floppy_image_filename,
            CONF.irmc.remote_image_user_name,
            CONF.irmc.remote_image_user_password)

        irmc_client(fd_set_params, async=False)
        irmc_client(scci.MOUNT_FD, async=False)

    except scci.SCCIClientError as irmc_exception:
        LOG.exception(
            "Error while inserting virtual floppy "
            "into node %(uuid)s. Error: %(error)s", {
                'uuid': node.uuid,
                'error': irmc_exception
            })
        operation = _("Inserting virtual floppy")
        raise exception.IRMCOperationError(operation=operation,
                                           error=irmc_exception)

    LOG.info("Attached virtual floppy successfully" " for node %s", node.uuid)
Ejemplo n.º 4
0
def _attach_virtual_cd(node, bootable_iso_filename):
    """Attaches the given url as virtual media on the node.

    :param node: an ironic node object.
    :param bootable_iso_filename: a bootable ISO image to attach to.
        The iso file should be present in NFS/CIFS server.
    :raises: IRMCOperationError if attaching virtual media failed.
    """
    try:
        irmc_client = irmc_common.get_irmc_client(node)

        cd_set_params = scci.get_virtual_cd_set_params_cmd(
            CONF.irmc.remote_image_server, CONF.irmc.remote_image_user_domain,
            scci.get_share_type(CONF.irmc.remote_image_share_type),
            CONF.irmc.remote_image_share_name, bootable_iso_filename,
            CONF.irmc.remote_image_user_name,
            CONF.irmc.remote_image_user_password)

        irmc_client(cd_set_params, async=False)
        irmc_client(scci.MOUNT_CD, async=False)

    except scci.SCCIClientError as irmc_exception:
        LOG.exception(
            "Error while inserting virtual cdrom "
            "into node %(uuid)s. Error: %(error)s", {
                'uuid': node.uuid,
                'error': irmc_exception
            })
        operation = _("Inserting virtual cdrom")
        raise exception.IRMCOperationError(operation=operation,
                                           error=irmc_exception)

    LOG.info("Attached virtual cdrom successfully" " for node %s", node.uuid)
Ejemplo n.º 5
0
def _set_power_state(task, target_state):
    """Turns the server power on/off or do a reboot.

    :param task: a TaskManager instance containing the node to act on.
    :param target_state: target state of the node.
    :raises: InvalidParameterValue if an invalid power state was specified.
    :raises: MissingParameterValue if some mandatory information
        is missing on the node
    :raises: IRMCOperationError on an error from SCCI
    """

    node = task.node
    irmc_client = irmc_common.get_irmc_client(node)

    try:
        irmc_client(STATES_MAP[target_state])

    except KeyError:
        msg = _("_set_power_state called with invalid power state "
                "'%s'") % target_state
        raise exception.InvalidParameterValue(msg)

    except scci.SCCIClientError as irmc_exception:
        LOG.error(
            _LE("iRMC set_power_state failed to set state to %(tstate)s "
                " for node %(node_id)s with error: %(error)s"), {
                    'tstate': target_state,
                    'node_id': node.uuid,
                    'error': irmc_exception
                })
        operation = _('iRMC set_power_state')
        raise exception.IRMCOperationError(operation=operation,
                                           error=irmc_exception)
Ejemplo n.º 6
0
def _restore_bios_config(task):
    """Restore BIOS config to a node.

    :param task: a TaskManager instance containing the node to act on.
    :raises: IRMCOperationError if the operation fails.
    """
    node_uuid = task.node.uuid

    # Get bios config stored in the node object
    bios_config = task.node.driver_internal_info.get('irmc_bios_config')
    if not bios_config:
        LOG.info(
            'Skipped operation "restore BIOS config" on node %s '
            'as the backup data not found.', node_uuid)
        return

    def _remove_bios_config(task, reboot_flag=False):
        """Remove backup bios config from the node."""
        internal_info = task.node.driver_internal_info
        internal_info.pop('irmc_bios_config', None)
        # NOTE(tiendc): If reboot flag is raised, then the BM will
        # reboot and cause a bug if the next clean step is in-band.
        # See https://storyboard.openstack.org/#!/story/2002731
        if reboot_flag:
            internal_info['cleaning_reboot'] = True
        task.node.driver_internal_info = internal_info
        task.node.save()

    irmc_info = irmc_common.parse_driver_info(task.node)

    try:
        # Restore bios config
        irmc.elcm.restore_bios_config(irmc_info, bios_config)
    except irmc.scci.SCCIError as e:
        # If the input bios config is not correct or corrupted, then
        # we should remove it from the node object.
        if isinstance(e, irmc.scci.SCCIInvalidInputError):
            _remove_bios_config(task)

        LOG.error(
            'Failed to restore BIOS config on node %(node)s. '
            'Error: %(error)s', {
                'node': node_uuid,
                'error': e
            })
        raise exception.IRMCOperationError(operation='restore BIOS config',
                                           error=e)

    # Remove the backup data after restoring
    _remove_bios_config(task, reboot_flag=True)

    LOG.info('BIOS config is restored successfully on node %s', node_uuid)

    # Change power state to ON as server is automatically
    # shutdown after the operation.
    manager_utils.node_power_action(task, states.POWER_ON)
Ejemplo n.º 7
0
def _get_raid_adapter(node):
    """Get the RAID adapter info on a RAID controller.

    :param node: an ironic node object.
    :returns: RAID adapter dictionary, None otherwise.
    :raises: IRMCOperationError on an error from python-scciclient.
    """
    irmc_info = node.driver_info
    LOG.info('iRMC driver is gathering RAID adapter info for node %s',
             node.uuid)
    try:
        return client.elcm.get_raid_adapter(irmc_info)
    except client.elcm.ELCMProfileNotFound:
        reason = ('Cannot find any RAID profile in "%s"' % node.uuid)
        raise exception.IRMCOperationError(operation='RAID config',
                                           error=reason)
Ejemplo n.º 8
0
    def _cleanup_boot_from_volume(self, task, reboot=False):
        """Clear remote boot configuration.

        :param task: a task from TaskManager.
        :param reboot: True if reboot node soon
        :raises: IRMCOperationError if iRMC operation failed
        """
        irmc_info = irmc_common.parse_driver_info(task.node)
        try:
            viom_conf = viom.VIOMConfiguration(irmc_info, task.node.uuid)
            viom_conf.terminate(reboot=reboot)
        except scci.SCCIError as e:
            LOG.error('iRMC failed to terminate VIOM configuration from '
                      'node %(node)s: %(error)s', {'node': task.node.uuid,
                                                   'error': e})
            raise exception.IRMCOperationError(operation='Terminate VIOM',
                                               error=e)
Ejemplo n.º 9
0
def get_secure_boot_mode(node):
    """Get the current secure boot mode.

    :param node: An ironic node object.
    :raises: UnsupportedDriverExtension if secure boot is not present.
    :raises: IRMCOperationError if the operation fails.
    """
    driver_info = parse_driver_info(node)

    try:
        return elcm.get_secure_boot_mode(driver_info)
    except elcm.SecureBootConfigNotFound:
        raise exception.UnsupportedDriverExtension(
            driver=node.driver, extension='get_secure_boot_state')
    except scci.SCCIError as irmc_exception:
        LOG.error("Failed to get secure boot for node %s", node.uuid)
        raise exception.IRMCOperationError(
            operation=_("getting secure boot mode"), error=irmc_exception)
Ejemplo n.º 10
0
def _delete_raid_adapter(node):
    """Delete the RAID adapter info on a RAID controller.

    :param node: an ironic node object.
    :raises: IRMCOperationError if SCCI failed from python-scciclient.
    """

    irmc_info = node.driver_info

    try:
        client.elcm.delete_raid_configuration(irmc_info)
    except client.scci.SCCIClientError as exc:
        LOG.error(
            'iRMC driver failed to delete RAID configuration '
            'for node %(node_uuid)s. Reason: %(error)s.', {
                'node_uuid': node.uuid,
                'error': exc
            })
        raise exception.IRMCOperationError(operation='RAID config', error=exc)
Ejemplo n.º 11
0
    def inject_nmi(self, task):
        """Inject NMI, Non Maskable Interrupt.

        Inject NMI (Non Maskable Interrupt) for a node immediately.

        :param task: A TaskManager instance containing the node to act on.
        :raises: IRMCOperationError on an error from SCCI
        :returns: None

        """
        node = task.node
        irmc_client = irmc_common.get_irmc_client(node)
        try:
            irmc_client(irmc.scci.POWER_RAISE_NMI)
        except irmc.scci.SCCIClientError as err:
            LOG.error('iRMC Inject NMI failed for node %(node)s: %(err)s.',
                      {'node': node.uuid, 'err': err})
            raise exception.IRMCOperationError(
                operation=irmc.scci.POWER_RAISE_NMI, error=err)
Ejemplo n.º 12
0
    def apply_configuration(self, task, settings):
        """Applies BIOS configuration on the given node.

        This method takes the BIOS settings from the settings param and
        applies BIOS configuration on the given node.
        After the BIOS configuration is done, self.cache_bios_settings() may
        be called to sync the node's BIOS-related information with the BIOS
        configuration applied on the node.
        It will also validate the given settings before applying any
        settings and manage failures when setting an invalid BIOS config.
        In the case of needing password to update the BIOS config, it will be
        taken from the driver_info properties.

        :param task: a TaskManager instance.
        :param settings: Dictionary containing the BIOS configuration. It
            may be an empty dictionary as well.
        :raises: IRMCOperationError,if apply bios settings failed.
        """

        irmc_info = irmc_common.parse_driver_info(task.node)

        try:
            LOG.info(
                'Apply BIOS configuration for node %(node_uuid)s: '
                '%(settings)s', {
                    'settings': settings,
                    'node_uuid': task.node.uuid
                })
            irmc.elcm.set_bios_configuration(irmc_info, settings)
            # NOTE(trungnv): Fix failed cleaning during rebooting node
            # when combine OOB and IB steps in manual clean.
            self._resume_cleaning(task)
        except irmc.scci.SCCIError as e:
            LOG.error(
                'Failed to apply BIOS configuration on node '
                '%(node_uuid)s. Error: %(error)s', {
                    'node_uuid': task.node.uuid,
                    'error': e
                })
            raise exception.IRMCOperationError(
                operation='Apply BIOS configuration', error=e)
Ejemplo n.º 13
0
def _detach_virtual_fd(node):
    """Detaches virtual media floppy on the node.

    :param node: an ironic node object.
    :raises: IRMCOperationError if eject virtual media floppy failed.
    """
    try:
        irmc_client = irmc_common.get_irmc_client(node)

        irmc_client(scci.UNMOUNT_FD)

    except scci.SCCIClientError as irmc_exception:
        LOG.exception(_LE("Error while ejecting virtual floppy "
                          "from node %(uuid)s. Error: %(error)s"),
                      {'uuid': node.uuid, 'error': irmc_exception})
        operation = _("Ejecting virtual floppy")
        raise exception.IRMCOperationError(operation=operation,
                                           error=irmc_exception)

    LOG.info(_LI("Detached virtual floppy successfully"
                 " for node %s"), node.uuid)
Ejemplo n.º 14
0
def set_secure_boot_mode(node, enable):
    """Enable or disable UEFI Secure Boot

    :param node: An ironic node object.
    :param enable: Boolean value. True if the secure boot to be
        enabled.
    :raises: IRMCOperationError if the operation fails.
    """
    driver_info = parse_driver_info(node)

    try:
        elcm.set_secure_boot_mode(driver_info, enable)
        LOG.info("Set secure boot to %(flag)s for node %(node)s", {
            'flag': enable,
            'node': node.uuid
        })
    except scci.SCCIError as irmc_exception:
        LOG.error("Failed to set secure boot to %(flag)s for node %(node)s", {
            'flag': enable,
            'node': node.uuid
        })
        raise exception.IRMCOperationError(
            operation=_("setting secure boot mode"), error=irmc_exception)
Ejemplo n.º 15
0
def backup_bios_config(task):
    """Backup BIOS config from a node.

    :param task: a TaskManager instance containing the node to act on.
    :raises: IRMCOperationError on failure.
    """
    node_uuid = task.node.uuid

    # Skip this operation if the clean step 'restore' is disabled
    if CONF.irmc.clean_priority_restore_irmc_bios_config == 0:
        LOG.debug(
            'Skipped the operation backup_BIOS_config for node %s '
            'as the clean step restore_BIOS_config is disabled.', node_uuid)
        return

    irmc_info = irmc_common.parse_driver_info(task.node)

    try:
        # Backup bios config
        result = irmc.elcm.backup_bios_config(irmc_info)
    except irmc.scci.SCCIError as e:
        LOG.error(
            'Failed to backup BIOS config for node %(node)s. '
            'Error: %(error)s', {
                'node': node_uuid,
                'error': e
            })
        raise exception.IRMCOperationError(operation='backup BIOS config',
                                           error=e)

    # Save bios config into the driver_internal_info
    internal_info = task.node.driver_internal_info
    internal_info['irmc_bios_config'] = result['bios_config']
    task.node.driver_internal_info = internal_info
    task.node.save()

    LOG.info('BIOS config is backed up successfully for node %s', node_uuid)
Ejemplo n.º 16
0
def _wait_power_state(task, target_state, timeout=None):
    """Wait for having changed to the target power state.

    :param task: A TaskManager instance containing the node to act on.
    :raises: IRMCOperationError if the target state acknowledge failed.
    :raises: SNMPFailure if SNMP request failed.
    """
    node = task.node
    d_info = irmc_common.parse_driver_info(node)
    snmp_client = snmp.SNMPClient(d_info['irmc_address'],
                                  d_info['irmc_snmp_port'],
                                  d_info['irmc_snmp_version'],
                                  d_info['irmc_snmp_community'],
                                  d_info['irmc_snmp_security'])

    interval = CONF.irmc.snmp_polling_interval
    retry_timeout_soft = timeout or CONF.conductor.soft_power_off_timeout
    max_retry = int(retry_timeout_soft / interval)

    def _wait(mutable):
        mutable['boot_status_value'] = snmp_client.get(BOOT_STATUS_OID)
        LOG.debug(
            "iRMC SNMP agent of %(node_id)s returned "
            "boot status value %(bootstatus)s on attempt %(times)s.", {
                'node_id': node.uuid,
                'bootstatus': BOOT_STATUS[mutable['boot_status_value']],
                'times': mutable['times']
            })

        if _is_expected_power_state(target_state,
                                    mutable['boot_status_value']):
            mutable['state'] = target_state
            raise loopingcall.LoopingCallDone()

        mutable['times'] += 1
        if mutable['times'] > max_retry:
            mutable['state'] = states.ERROR
            raise loopingcall.LoopingCallDone()

    store = {'state': None, 'times': 0, 'boot_status_value': None}
    timer = loopingcall.FixedIntervalLoopingCall(_wait, store)
    timer.start(interval=interval).wait()

    if store['state'] == target_state:
        # iRMC acknowledged the target state
        node.last_error = None
        node.power_state = (states.POWER_OFF if target_state
                            == states.SOFT_POWER_OFF else states.POWER_ON)
        node.target_power_state = states.NOSTATE
        node.save()
        LOG.info(
            'iRMC successfully set node %(node_id)s '
            'power state to %(bootstatus)s.', {
                'node_id': node.uuid,
                'bootstatus': BOOT_STATUS[store['boot_status_value']]
            })
    else:
        # iRMC failed to acknowledge the target state
        last_error = (_('iRMC returned unexpected boot status value %s') %
                      BOOT_STATUS[store['boot_status_value']])
        node.last_error = last_error
        node.power_state = states.ERROR
        node.target_power_state = states.NOSTATE
        node.save()
        LOG.error(
            'iRMC failed to acknowledge the target state for node '
            '%(node_id)s. Error: %(last_error)s', {
                'node_id': node.uuid,
                'last_error': last_error
            })
        error = _('unexpected boot status value')
        raise exception.IRMCOperationError(operation=target_state, error=error)
Ejemplo n.º 17
0
def _validate_physical_disks(node, logical_disks):
    """Validate physical disks on a RAID configuration.

    :param node: an ironic node object.
    :param logical_disks: RAID info to set RAID configuration
    :raises: IRMCOperationError on an error.
    """
    raid_adapter = _get_raid_adapter(node)
    physical_disk_dict = _get_physical_disk(node)
    if raid_adapter is None:
        reason = ('Cannot find any raid profile in "%s"' % node.uuid)
        raise exception.IRMCOperationError(operation='RAID config',
                                           error=reason)
    if physical_disk_dict is None:
        reason = ('Cannot find any physical disks in "%s"' % node.uuid)
        raise exception.IRMCOperationError(operation='RAID config',
                                           error=reason)
    valid_disks = raid_adapter['Server']['HWConfigurationIrmc']['Adapters'][
        'RAIDAdapter'][0]['PhysicalDisks']
    if valid_disks is None:
        reason = ('Cannot find any HDD over in the node "%s"' % node.uuid)
        raise exception.IRMCOperationError(operation='RAID config',
                                           error=reason)
    valid_disk_slots = [slot['Slot'] for slot in valid_disks['PhysicalDisk']]
    remain_valid_disk_slots = list(valid_disk_slots)
    number_of_valid_disks = len(valid_disk_slots)
    used_valid_disk_slots = []

    for disk in logical_disks:
        # Check raid_level value in the target_raid_config of node
        if disk.get('raid_level') not in RAID_LEVELS:
            reason = ('RAID level is not supported: "%s"' %
                      disk.get('raid_level'))
            raise exception.IRMCOperationError(operation='RAID config',
                                               error=reason)

        min_disk_value = RAID_LEVELS[disk['raid_level']]['min_disks']
        max_disk_value = RAID_LEVELS[disk['raid_level']]['max_disks']
        remain_valid_disks = number_of_valid_disks - min_disk_value
        number_of_valid_disks = number_of_valid_disks - min_disk_value

        if remain_valid_disks < 0:
            reason = ('Physical disks do not enough slots for raid "%s"' %
                      disk['raid_level'])
            raise exception.IRMCOperationError(operation='RAID config',
                                               error=reason)

        if 'physical_disks' in disk:
            type_of_disks = []
            number_of_physical_disks = len(disk['physical_disks'])
            # Check number of physical disks along with raid level
            if number_of_physical_disks > max_disk_value:
                reason = ("Too many disks requested for RAID level %(level)s, "
                          "maximum is %(max)s", {
                              'level': disk['raid_level'],
                              'max': max_disk_value
                          })
                raise exception.InvalidParameterValue(err=reason)
            if number_of_physical_disks < min_disk_value:
                reason = ("Not enough disks requested for RAID level "
                          "%(level)s, minimum is %(min)s ", {
                              'level': disk['raid_level'],
                              'min': min_disk_value
                          })
                raise exception.IRMCOperationError(operation='RAID config',
                                                   error=reason)
            # Check physical disks in valid disk slots
            for phys_disk in disk['physical_disks']:
                if int(phys_disk) not in valid_disk_slots:
                    reason = ("Incorrect physical disk %(disk)s, correct are "
                              "%(valid)s", {
                                  'disk': phys_disk,
                                  'valid': valid_disk_slots
                              })
                    raise exception.IRMCOperationError(operation='RAID config',
                                                       error=reason)
                type_of_disks.append(physical_disk_dict[int(phys_disk)])
                if physical_disk_dict[int(phys_disk)] != type_of_disks[0]:
                    reason = ('Cannot create RAID configuration with '
                              'different hard drives type %s' %
                              physical_disk_dict[int(phys_disk)])
                    raise exception.IRMCOperationError(operation='RAID config',
                                                       error=reason)
                # Check physical disk values with used disk slots
                if int(phys_disk) in used_valid_disk_slots:
                    reason = (
                        "Disk %s is already used in a RAID configuration" %
                        disk['raid_level'])
                    raise exception.IRMCOperationError(operation='RAID config',
                                                       error=reason)

                used_valid_disk_slots.append(int(phys_disk))
                remain_valid_disk_slots.remove(int(phys_disk))

        if disk['size_gb'] != 'MAX':
            # Validate size_gb value input
            _validate_logical_drive_capacity(disk, valid_disks)