Пример #1
0
def mkfs(fs, path, label=None):
    """Format a file or block device

    :param fs: Filesystem type (examples include 'swap', 'ext3', 'ext4'
               'btrfs', etc.)
    :param path: Path to file or block device to format
    :param label: Volume label to use
    """
    if fs == 'swap':
        args = ['mkswap']
    else:
        args = ['mkfs', '-t', fs]
    # add -F to force no interactive execute on non-block device.
    if fs in ('ext3', 'ext4'):
        args.extend(['-F'])
    if label:
        if fs in ('msdos', 'vfat'):
            label_opt = '-n'
        else:
            label_opt = '-L'
        args.extend([label_opt, label])
    args.append(path)
    try:
        execute(*args, run_as_root=True, use_standard_locale=True)
    except processutils.ProcessExecutionError as e:
        with excutils.save_and_reraise_exception() as ctx:
            if os.strerror(errno.ENOENT) in e.stderr:
                ctx.reraise = False
                LOG.exception(_LE('Failed to make file system. '
                                  'File system %s is not supported.'), fs)
                raise exception.FileSystemNotSupported(fs=fs)
            else:
                LOG.exception(_LE('Failed to create a file system '
                                  'in %(path)s. Error: %(error)s'),
                              {'path': path, 'error': e})
Пример #2
0
    def update_port_address(self, port_id, address, token=None):
        """Update a port's mac address.

        :param port_id: Neutron port id.
        :param address: new MAC address.
        :param token: optional auth token.
        :raises: FailedToUpdateMacOnPort
        """
        client = neutron.get_client(token)
        port_req_body = {'port': {'mac_address': address}}

        current_binding = self._get_binding(client, port_id)
        if current_binding:
            binding_clean_body = {'port': {'binding:host_id': ''}}
            try:
                client.update_port(port_id, binding_clean_body)
            except neutron_client_exc.NeutronClientException:
                LOG.exception(_LE("Failed to remove the current binding from "
                                  "Neutron port %s."), port_id)
                raise exception.FailedToUpdateMacOnPort(port_id=port_id)

            port_req_body['port']['binding:host_id'] = current_binding

        try:
            neutron.get_client(token).update_port(port_id, port_req_body)
        except neutron_client_exc.NeutronClientException:
            LOG.exception(_LE("Failed to update MAC address on Neutron "
                              "port %s."), port_id)
            raise exception.FailedToUpdateMacOnPort(port_id=port_id)
Пример #3
0
    def reboot(self, task, helper=None):
        """Cycles the power to a node.

        :param task: a TaskManager instance.
        :param helper: ucs helper instance.
        :raises: UcsOperationError on error from UCS Client.
        :raises: PowerStateFailure if the final state of the node is not
            POWER_ON.
        """
        try:
            ucs_power_handle = ucs_power.UcsPower(helper)
            ucs_power_handle.reboot()
        except ucs_error.UcsOperationError as ucs_exception:
            LOG.error(_LE("%(driver)s: driver failed to reset node %(uuid)s "
                          "power state."),
                      {'driver': task.node.driver, 'uuid': task.node.uuid})
            operation = _("rebooting")
            raise exception.UcsOperationError(operation=operation,
                                              error=ucs_exception,
                                              node=task.node.uuid)

        state = _wait_for_state_change(states.POWER_ON, ucs_power_handle)
        if state != states.POWER_ON:
            timeout = CONF.cisco_ucs.action_interval * CONF.cisco_ucs.max_retry
            LOG.error(_LE("%(driver)s: driver failed to reboot node %(uuid)s "
                          "within %(timeout)s seconds."),
                      {'driver': task.node.driver,
                       'uuid': task.node.uuid, 'timeout': timeout})
            raise exception.PowerStateFailure(pstate=states.POWER_ON)
Пример #4
0
def set_failed_state(task, msg):
    """Sets the deploy status as failed with relevant messages.

    This method sets the deployment as fail with the given message.
    It sets node's provision_state to DEPLOYFAIL and updates last_error
    with the given error message. It also powers off the baremetal node.

    :param task: a TaskManager instance containing the node to act on.
    :param msg: the message to set in last_error of the node.
    """
    node = task.node
    try:
        task.process_event('fail')
    except exception.InvalidState:
        msg2 = (_LE('Internal error. Node %(node)s in provision state '
                    '"%(state)s" could not transition to a failed state.')
                % {'node': node.uuid, 'state': node.provision_state})
        LOG.exception(msg2)

    try:
        manager_utils.node_power_action(task, states.POWER_OFF)
    except Exception:
        msg2 = (_LE('Node %s failed to power off while handling deploy '
                    'failure. This may be a serious condition. Node '
                    'should be removed from Ironic or put in maintenance '
                    'mode until the problem is resolved.') % node.uuid)
        LOG.exception(msg2)

    # NOTE(deva): node_power_action() erases node.last_error
    #             so we need to set it here.
    node.last_error = msg
    node.save()
Пример #5
0
    def vendor_passthru(self, task, **kwargs):
        """A node that knows its UUID should heartbeat to this passthru.

        It will get its node object back, with what Ironic thinks its provision
        state is and the target provision state is.
        """
        method = kwargs['method']  # Existence checked in mixin
        if method not in self.vendor_routes:
            raise exception.InvalidParameterValue(_('No handler for method '
                                                    '%s') % method)
        func = self.vendor_routes[method]
        try:
            return func(task, **kwargs)
        except exception.IronicException as e:
            with excutils.save_and_reraise_exception():
                # log this because even though the exception is being
                # reraised, it won't be handled if it is an async. call.
                LOG.exception(_LE('vendor_passthru failed with method %s'),
                              method)
        except Exception as e:
            # catch-all in case something bubbles up here
            # log this because even though the exception is being
            # reraised, it won't be handled if it is an async. call.
            LOG.exception(_LE('vendor_passthru failed with method %s'), method)
            raise exception.VendorPassthruException(message=e)
Пример #6
0
def cleanup_vmedia_boot(task):
    """Cleans a node after a virtual media boot.

    This method cleans up a node after a virtual media boot. It deletes the
    floppy image if it exists in CONF.ilo.swift_ilo_container. It also
    ejects both virtual media cdrom and virtual media floppy.

    :param task: a TaskManager instance containing the node to act on.
    """
    LOG.debug("Cleaning up node %s after virtual media boot", task.node.uuid)

    container = CONF.ilo.swift_ilo_container
    object_name = _get_floppy_image_name(task.node)
    try:
        swift_api = swift.SwiftAPI()
        swift_api.delete_object(container, object_name)
    except exception.SwiftOperationError as e:
        LOG.exception(_LE("Error while deleting %(object_name)s from "
                          "%(container)s. Error: %(error)s"),
                      {'object_name': object_name, 'container': container,
                       'error': e})

    ilo_object = get_ilo_object(task.node)
    for device in ('FLOPPY', 'CDROM'):
        try:
            ilo_object.eject_virtual_media(device)
        except ilo_client.IloError as ilo_exception:
            LOG.exception(_LE("Error while ejecting virtual media %(device)s "
                              "from node %(uuid)s. Error: %(error)s"),
                          {'device': device, 'uuid': task.node.uuid,
                           'error': ilo_exception})
Пример #7
0
    def _get_fixed_ip_address(self, port_uuid, client):
        """Get a port's fixed ip address.

        :param port_uuid: Neutron port id.
        :param client: Neutron client instance.
        :returns: Neutron port ip address.
        :raises: FailedToGetIPAddressOnPort
        :raises: InvalidIPv4Address
        """
        ip_address = None
        try:
            neutron_port = client.show_port(port_uuid).get("port")
        except neutron_client_exc.NeutronClientException:
            LOG.exception(_LE("Failed to Get IP address on Neutron port %s."), port_uuid)
            raise exception.FailedToGetIPAddressOnPort(port_id=port_uuid)

        fixed_ips = neutron_port.get("fixed_ips")

        # NOTE(faizan) At present only the first fixed_ip assigned to this
        # neutron port will be used, since nova allocates only one fixed_ip
        # for the instance.
        if fixed_ips:
            ip_address = fixed_ips[0].get("ip_address", None)

        if ip_address:
            if netutils.is_valid_ipv4(ip_address):
                return ip_address
            else:
                LOG.error(_LE("Neutron returned invalid IPv4 address %s."), ip_address)
                raise exception.InvalidIPv4Address(ip_address=ip_address)
        else:
            LOG.error(_LE("No IP address assigned to Neutron port %s."), port_uuid)
            raise exception.FailedToGetIPAddressOnPort(port_id=port_uuid)
Пример #8
0
def _check_status(task):
    """Check inspection status for node given by a task."""
    node = task.node
    if node.provision_state != states.INSPECTING:
        return
    if not isinstance(task.driver.inspect, Inspector):
        return

    LOG.debug('Calling to inspector to check status of node %s',
              task.node.uuid)

    # NOTE(dtantsur): periodic tasks do not have proper tokens in context
    task.context.auth_token = keystone.get_admin_auth_token()
    try:
        status = _call_inspector(client.get_status, node.uuid, task.context)
    except Exception:
        # NOTE(dtantsur): get_status should not normally raise
        # let's assume it's a transient failure and retry later
        LOG.exception(_LE('Unexpected exception while getting '
                          'inspection status for node %s, will retry later'),
                      node.uuid)
        return

    if status.get('error'):
        LOG.error(_LE('Inspection failed for node %(uuid)s '
                      'with error: %(err)s'),
                  {'uuid': node.uuid, 'err': status['error']})
        node.last_error = (_('ironic-inspector inspection failed: %s')
                           % status['error'])
        task.process_event('fail')
    elif status.get('finished'):
        LOG.info(_LI('Inspection finished successfully for node %s'),
                 node.uuid)
        task.process_event('done')
Пример #9
0
def _iscsi_setup_and_handle_errors(address, port, iqn, lun):
    """Function that yields an iSCSI target device to work on.

    :param address: The iSCSI IP address.
    :param port: The iSCSI port number.
    :param iqn: The iSCSI qualified name.
    :param lun: The iSCSI logical unit number.
    """
    dev = get_dev(address, port, iqn, lun)
    discovery(address, port)
    login_iscsi(address, port, iqn)
    if not is_block_device(dev):
        raise exception.InstanceDeployFailure(_("Parent device '%s' not found")
                                              % dev)
    try:
        yield dev
    except processutils.ProcessExecutionError as err:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE("Deploy to address %s failed."), address)
            LOG.error(_LE("Command: %s"), err.cmd)
            LOG.error(_LE("StdOut: %r"), err.stdout)
            LOG.error(_LE("StdErr: %r"), err.stderr)
    except exception.InstanceDeployFailure as e:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE("Deploy to address %s failed."), address)
            LOG.error(e)
    finally:
        logout_iscsi(address, port, iqn)
        delete_iscsi(address, port, iqn)
Пример #10
0
def create_isolinux_image_for_bios(output_file, kernel, ramdisk, kernel_params=None):
    """Creates an isolinux image on the specified file.

    Copies the provided kernel, ramdisk to a directory, generates the isolinux
    configuration file using the kernel parameters provided, and then generates
    a bootable ISO image.

    :param output_file: the path to the file where the iso image needs to be
        created.
    :param kernel: the kernel to use.
    :param ramdisk: the ramdisk to use.
    :param kernel_params: a list of strings(each element being a string like
        'K=V' or 'K' or combination of them like 'K1=V1,K2,...') to be added
        as the kernel cmdline.
    :raises: ImageCreationFailed, if image creation failed while copying files
        or while running command to generate iso.
    """
    ISOLINUX_BIN = "isolinux/isolinux.bin"
    ISOLINUX_CFG = "isolinux/isolinux.cfg"

    options = {"kernel": "/vmlinuz", "ramdisk": "/initrd"}

    with utils.tempdir() as tmpdir:
        files_info = {kernel: "vmlinuz", ramdisk: "initrd", CONF.isolinux_bin: ISOLINUX_BIN}
        try:
            _create_root_fs(tmpdir, files_info)
        except (OSError, IOError) as e:
            LOG.exception(_LE("Creating the filesystem root failed."))
            raise exception.ImageCreationFailed(image_type="iso", error=e)

        cfg = _generate_cfg(kernel_params, CONF.isolinux_config_template, options)

        isolinux_cfg = os.path.join(tmpdir, ISOLINUX_CFG)
        utils.write_to_file(isolinux_cfg, cfg)

        try:
            utils.execute(
                "mkisofs",
                "-r",
                "-V",
                "VMEDIA_BOOT_ISO",
                "-cache-inodes",
                "-J",
                "-l",
                "-no-emul-boot",
                "-boot-load-size",
                "4",
                "-boot-info-table",
                "-b",
                ISOLINUX_BIN,
                "-o",
                output_file,
                tmpdir,
            )
        except processutils.ProcessExecutionError as e:
            LOG.exception(_LE("Creating ISO image failed."))
            raise exception.ImageCreationFailed(image_type="iso", error=e)
Пример #11
0
def _mount_deploy_iso(deploy_iso, mountdir):
    """This function opens up the deploy iso used for deploy.

    :param: deploy_iso: path to the deploy iso where its
                        contents are fetched to.
    :raises: ImageCreationFailed if mount fails.
    :returns: a tuple consisting of - 1. a dictionary containing
                                         the values as required
                                         by create_isolinux_image,
                                      2. efiboot.img relative path, and
                                      3. grub.cfg relative path.

    """
    e_img_rel_path = None
    e_img_path = None
    grub_rel_path = None
    grub_path = None

    try:
        utils.mount(deploy_iso, mountdir, '-o', 'loop')
    except processutils.ProcessExecutionError as e:
        LOG.exception(_LE("mounting the deploy iso failed."))
        raise exception.ImageCreationFailed(image_type='iso', error=e)

    try:
        for (dir, subdir, files) in os.walk(mountdir):
            if 'efiboot.img' in files:
                e_img_path = os.path.join(dir, 'efiboot.img')
                e_img_rel_path = os.path.relpath(e_img_path,
                                                 mountdir)
            if 'grub.cfg' in files:
                grub_path = os.path.join(dir, 'grub.cfg')
                grub_rel_path = os.path.relpath(grub_path,
                                                mountdir)
    except (OSError, IOError) as e:
        LOG.exception(_LE("examining the deploy iso failed."))
        _umount_without_raise(mountdir)
        raise exception.ImageCreationFailed(image_type='iso', error=e)

    # check if the variables are assigned some values or not during
    # walk of the mountdir.
    if not (e_img_path and e_img_rel_path and grub_path and grub_rel_path):
        error = (_("Deploy iso didn't contain efiboot.img or grub.cfg"))
        _umount_without_raise(mountdir)
        raise exception.ImageCreationFailed(image_type='iso', error=error)

    uefi_path_info = {e_img_path: e_img_rel_path,
                      grub_path: grub_rel_path}

    # Returning a tuple as it makes the code simpler and clean.
    # uefi_path_info: is needed by the caller for _create_root_fs to create
    # appropriate directory structures for uefi boot iso.
    # grub_rel_path: is needed to copy the new grub.cfg generated using
    # generate_cfg() to the same directory path structure where it was
    # present in deploy iso. This path varies for different OS vendors.
    # e_img_rel_path: is required by mkisofs to generate boot iso.
    return uefi_path_info, e_img_rel_path, grub_rel_path
Пример #12
0
def _run_virtualbox_method(node, ironic_method, vm_object_method,
                           *call_args, **call_kwargs):
    """Runs a method of pyremotevbox.vbox.VirtualMachine

    This runs a method from pyremotevbox.vbox.VirtualMachine.
    The VirtualMachine method to be invoked and the argument(s) to be
    passed to it are to be provided.

    :param node: an Ironic Node object.
    :param ironic_method: the Ironic method which called
        '_run_virtualbox_method'. This is used for logging only.
    :param vm_object_method: The method on the VirtualMachine object
        to be called.
    :param call_args: The args to be passed to 'vm_object_method'.
    :param call_kwargs: The kwargs to be passed to the 'vm_object_method'.
    :returns: The value returned by 'vm_object_method'
    :raises: VirtualBoxOperationFailed, if execution of 'vm_object_method'
        failed.
    :raises: InvalidParameterValue,
        - if 'vm_object_method' is not a valid 'VirtualMachine' method.
        - if some parameter(s) have invalid value(s) in the node's driver_info.
    :raises: MissingParameterValue, if some required parameter(s) are missing
        in the node's driver_info.
    :raises: pyremotevbox.exception.VmInWrongPowerState, if operation cannot
        be performed when vm is in the current power state.
    """
    driver_info = _parse_driver_info(node)
    try:
        host = virtualbox.VirtualBoxHost(**driver_info)
        vm_object = host.find_vm(driver_info['vmname'])
    except virtualbox_exc.PyRemoteVBoxException as exc:
        LOG.error(_LE("Failed while creating a VirtualMachine object for "
                      "node %(node_id)s. Error: %(error)s."),
                  {'node_id': node.uuid, 'error': exc})
        raise exception.VirtualBoxOperationFailed(operation=vm_object_method,
                                                  error=exc)

    try:
        func = getattr(vm_object, vm_object_method)
    except AttributeError:
        error_msg = _("Invalid VirtualMachine method '%s' passed "
                      "to '_run_virtualbox_method'.")
        raise exception.InvalidParameterValue(error_msg % vm_object_method)

    try:
        return func(*call_args, **call_kwargs)
    except virtualbox_exc.PyRemoteVBoxException as exc:
        error_msg = _LE("'%(ironic_method)s' failed for node %(node_id)s with "
                        "error: %(error)s.")
        LOG.error(error_msg, {'ironic_method': ironic_method,
                              'node_id': node.uuid,
                              'error': exc})
        raise exception.VirtualBoxOperationFailed(operation=vm_object_method,
                                                  error=exc)
Пример #13
0
def destroy_disk_metadata(dev, node_uuid):
    """Destroy metadata structures on node's disk.

       Ensure that node's disk appears to be blank without zeroing the entire
       drive. To do this we will zero:
       - the first 18KiB to clear MBR / GPT data
       - the last 18KiB to clear GPT and other metadata like: LVM, veritas,
         MDADM, DMRAID, ...
    """
    # NOTE(NobodyCam): This is needed to work around bug:
    # https://bugs.launchpad.net/ironic/+bug/1317647
    LOG.debug("Start destroy disk metadata for node %(node)s.", {"node": node_uuid})
    try:
        utils.execute("dd", "if=/dev/zero", "of=%s" % dev, "bs=512", "count=36", run_as_root=True, check_exit_code=[0])
    except processutils.ProcessExecutionError as err:
        with excutils.save_and_reraise_exception():
            LOG.error(
                _LE("Failed to erase beginning of disk for node " "%(node)s. Command: %(command)s. Error: %(error)s."),
                {"node": node_uuid, "command": err.cmd, "error": err.stderr},
            )

    # now wipe the end of the disk.
    # get end of disk seek value
    try:
        block_sz = get_dev_block_size(dev)
    except processutils.ProcessExecutionError as err:
        with excutils.save_and_reraise_exception():
            LOG.error(
                _LE("Failed to get disk block count for node %(node)s. " "Command: %(command)s. Error: %(error)s."),
                {"node": node_uuid, "command": err.cmd, "error": err.stderr},
            )
    else:
        seek_value = block_sz - 36
        try:
            utils.execute(
                "dd",
                "if=/dev/zero",
                "of=%s" % dev,
                "bs=512",
                "count=36",
                "seek=%d" % seek_value,
                run_as_root=True,
                check_exit_code=[0],
            )
        except processutils.ProcessExecutionError as err:
            with excutils.save_and_reraise_exception():
                LOG.error(
                    _LE(
                        "Failed to erase the end of the disk on node "
                        "%(node)s. Command: %(command)s. "
                        "Error: %(error)s."
                    ),
                    {"node": node_uuid, "command": err.cmd, "error": err.stderr},
                )
Пример #14
0
def deploy(address, port, iqn, lun, image_path,
           root_mb, swap_mb, ephemeral_mb, ephemeral_format, node_uuid,
           preserve_ephemeral=False, configdrive=None):
    """All-in-one function to deploy a node.

    :param address: The iSCSI IP address.
    :param port: The iSCSI port number.
    :param iqn: The iSCSI qualified name.
    :param lun: The iSCSI logical unit number.
    :param image_path: Path for the instance's disk image.
    :param root_mb: Size of the root partition in megabytes.
    :param swap_mb: Size of the swap partition in megabytes.
    :param ephemeral_mb: Size of the ephemeral partition in megabytes. If 0,
        no ephemeral partition will be created.
    :param ephemeral_format: The type of file system to format the ephemeral
        partition.
    :param node_uuid: node's uuid. Used for logging.
    :param preserve_ephemeral: If True, no filesystem is written to the
        ephemeral block device, preserving whatever content it had (if the
        partition table has not changed).
    :param configdrive: Optional. Base64 encoded Gzipped configdrive content
                        or configdrive HTTP URL.
    :returns: the UUID of the root partition.
    """
    dev = get_dev(address, port, iqn, lun)
    image_mb = get_image_mb(image_path)
    if image_mb > root_mb:
        root_mb = image_mb
    discovery(address, port)
    login_iscsi(address, port, iqn)
    try:
        root_uuid = work_on_disk(dev, root_mb, swap_mb, ephemeral_mb,
                                 ephemeral_format, image_path, node_uuid,
                                 preserve_ephemeral=preserve_ephemeral,
                                 configdrive=configdrive)
    except processutils.ProcessExecutionError as err:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE("Deploy to address %s failed."), address)
            LOG.error(_LE("Command: %s"), err.cmd)
            LOG.error(_LE("StdOut: %r"), err.stdout)
            LOG.error(_LE("StdErr: %r"), err.stderr)
    except exception.InstanceDeployFailure as e:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE("Deploy to address %s failed."), address)
            LOG.error(e)
    finally:
        logout_iscsi(address, port, iqn)
        delete_iscsi(address, port, iqn)

    return root_uuid
Пример #15
0
def _power_status(node):
    """Get the power status for a node.

    :param node: a node object.
    :returns: one of ironic.common.states POWER_OFF, POWER_ON or ERROR.
    :raises: AMTFailure.
    :raises: AMTConnectFailure.

    """
    client = amt_common.get_wsman_client(node)
    namespace = resource_uris.CIM_AssociatedPowerManagementService
    try:
        doc = client.wsman_get(namespace)
    except (exception.AMTFailure, exception.AMTConnectFailure) as e:
        with excutils.save_and_reraise_exception():
            LOG.exception(_LE("Failed to get power state for node %(node_id)s "
                              "with error: %(error)s."),
                          {'node_id': node.uuid, 'error': e})

    item = "PowerState"
    power_state = amt_common.xml_find(doc, namespace, item).text
    for state in AMT_POWER_MAP:
        if power_state == AMT_POWER_MAP[state]:
            return state
    return states.ERROR
Пример #16
0
def _set_power_state(node, target_state):
    """Set power state of the AMT Client.

    :param node: a node object.
    :param target_state: desired power state.
    :raises: AMTFailure
    :raises: AMTConnectFailure
    """
    client = amt_common.get_wsman_client(node)

    method = 'RequestPowerStateChange'
    options = pywsman.ClientOptions()
    options.add_selector('Name', 'Intel(r) AMT Power Management Service')

    doc = _generate_power_action_input(AMT_POWER_MAP[target_state])
    try:
        client.wsman_invoke(options, resource_uris.CIM_PowerManagementService,
                            method, doc)
    except (exception.AMTFailure, exception.AMTConnectFailure) as e:
        with excutils.save_and_reraise_exception():
            LOG.exception(_LE("Failed to set power state %(state)s for "
                              "node %(node_id)s with error: %(error)s."),
                          {'state': target_state, 'node_id': node.uuid,
                           'error': e})
    else:
        LOG.info(_LI("Power state set to %(state)s for node %(node_id)s"),
                 {'state': target_state, 'node_id': node.uuid})
Пример #17
0
def _get_power_status(node):
    """Get current power state of this node

    :param node: Ironic node one of :class:`ironic.db.models.Node`
    :raises: InvalidParameterValue if a seamicro parameter is invalid.
    :raises: MissingParameterValue if required seamicro parameters are
        missing.
    :raises: ServiceUnavailable on an error from SeaMicro Client.
    :returns: Power state of the given node
    """

    seamicro_info = _parse_driver_info(node)
    try:
        server = _get_server(seamicro_info)
        if not hasattr(server, 'active') or server.active is None:
            return states.ERROR
        if not server.active:
            return states.POWER_OFF
        elif server.active:
            return states.POWER_ON

    except seamicro_client_exception.NotFound:
        raise exception.NodeNotFound(node=node.uuid)
    except seamicro_client_exception.ClientException as ex:
        LOG.error(_LE("SeaMicro client exception %(msg)s for node %(uuid)s"),
                  {'msg': ex.message, 'uuid': node.uuid})
        raise exception.ServiceUnavailable(message=ex.message)
Пример #18
0
    def _wait(mutable):
        try:
            # Only issue power change command once
            if mutable['iter'] < 0:
                _exec_ipmitool(driver_info, "power %s" % state_name)
            else:
                mutable['power'] = _power_status(driver_info)
        except (exception.PasswordFileFailedToCreate,
                processutils.ProcessExecutionError,
                exception.IPMIFailure):
            # Log failures but keep trying
            LOG.warning(_LW("IPMI power %(state)s failed for node %(node)s."),
                        {'state': state_name, 'node': driver_info['uuid']})
        finally:
            mutable['iter'] += 1

        if mutable['power'] == target_state:
            raise loopingcall.LoopingCallDone()

        sleep_time = _sleep_time(mutable['iter'])
        if (sleep_time + mutable['total_time']) > CONF.ipmi.retry_timeout:
            # Stop if the next loop would exceed maximum retry_timeout
            LOG.error(_LE('IPMI power %(state)s timed out after '
                          '%(tries)s retries on node %(node_id)s.'),
                      {'state': state_name, 'tries': mutable['iter'],
                       'node_id': driver_info['uuid']})
            mutable['power'] = states.ERROR
            raise loopingcall.LoopingCallDone()
        else:
            mutable['total_time'] += sleep_time
            return sleep_time
Пример #19
0
    def bmc_reset(self, task, http_method, warm=True):
        """Reset BMC with IPMI command 'bmc reset (warm|cold)'.

        :param task: a TaskManager instance.
        :param http_method: the HTTP method used on the request.
        :param warm: boolean parameter to decide on warm or cold reset.
        :raises: IPMIFailure on an error from ipmitool.
        :raises: MissingParameterValue if a required parameter is missing.
        :raises: InvalidParameterValue when an invalid value is specified

        """
        node_uuid = task.node.uuid

        warm = strutils.bool_from_string(warm)
        if warm:
            warm_param = 'warm'
        else:
            warm_param = 'cold'

        LOG.debug('Doing %(warm)s BMC reset on node %(node)s',
                  {'warm': warm_param, 'node': node_uuid})
        driver_info = _parse_driver_info(task.node)
        cmd = 'bmc reset %s' % warm_param

        try:
            out, err = _exec_ipmitool(driver_info, cmd)
            LOG.debug('bmc reset returned stdout: %(stdout)s, stderr:'
                      ' %(stderr)s', {'stdout': out, 'stderr': err})
        except (exception.PasswordFileFailedToCreate,
                processutils.ProcessExecutionError) as e:
            LOG.exception(_LE('IPMI "bmc reset" failed for node %(node_id)s '
                          'with error: %(error)s.'),
                          {'node_id': node_uuid, 'error': e})
            raise exception.IPMIFailure(cmd=cmd)
Пример #20
0
    def set_node_vlan_id(self, task, **kwargs):
        """Sets a untagged vlan id for NIC 0 of node.

        @kwargs vlan_id: id of untagged vlan for NIC 0 of node
        """
        node = task.node
        vlan_id = kwargs.get('vlan_id')
        if not vlan_id:
            raise exception.MissingParameterValue(_("No vlan id provided"))

        seamicro_info = _parse_driver_info(node)
        try:
            server = _get_server(seamicro_info)

            # remove current vlan for server
            if len(server.nic['0']['untaggedVlan']) > 0:
                server.unset_untagged_vlan(server.nic['0']['untaggedVlan'])
            server = server.refresh(5)
            server.set_untagged_vlan(vlan_id)
        except seamicro_client_exception.ClientException as ex:
            LOG.error(_LE("SeaMicro client exception: %s"), ex.message)
            raise exception.VendorPassthruException(message=ex.message)

        properties = node.properties
        properties['seamicro_vlan_id'] = vlan_id
        node.properties = properties
        node.save()
Пример #21
0
def _check_for_config_job(node):
    """Check if a configuration job is already created.

    :param node: an ironic node object.
    :raises: DracClientError on an error from pywsman library.
    :raises: DracPendingConfigJobExists if the job is already created.

    """
    client = drac_client.get_wsman_client(node)
    try:
        doc = client.wsman_enumerate(resource_uris.DCIM_LifecycleJob)
    except exception.DracClientError as exc:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE('DRAC driver failed to list the configuration jobs '
                          'for node %(node_uuid)s. Reason: %(error)s.'),
                      {'node_uuid': node.uuid, 'error': exc})

    items = drac_common.find_xml(doc, 'DCIM_LifecycleJob',
                                 resource_uris.DCIM_LifecycleJob,
                                 find_all=True)
    for i in items:
        name = drac_common.find_xml(i, 'Name', resource_uris.DCIM_LifecycleJob)
        if TARGET_DEVICE not in name.text:
            continue

        job_status = drac_common.find_xml(i, 'JobStatus',
                                      resource_uris.DCIM_LifecycleJob).text
        # If job is already completed or failed we can
        # create another one.
        # Job Control Documentation: http://goo.gl/o1dDD3 (Section 7.2.3.2)
        if job_status.lower() not in ('completed', 'failed'):
            job_id = drac_common.find_xml(i, 'InstanceID',
                                          resource_uris.DCIM_LifecycleJob).text
            raise exception.DracPendingConfigJobExists(job_id=job_id,
                                                       target=TARGET_DEVICE)
Пример #22
0
    def update_port_dhcp_opts(self, port_id, dhcp_options, token=None):
        """Update a port's attributes.

        Update one or more DHCP options on the specified port.
        For the relevant API spec, see
        http://docs.openstack.org/api/openstack-network/2.0/content/extra-dhc-opt-ext-update.html  # noqa

        :param port_id: designate which port these attributes
                        will be applied to.
        :param dhcp_options: this will be a list of dicts, e.g.

                             ::

                              [{'opt_name': 'bootfile-name',
                                'opt_value': 'pxelinux.0'},
                               {'opt_name': 'server-ip-address',
                                'opt_value': '123.123.123.456'},
                               {'opt_name': 'tftp-server',
                                'opt_value': '123.123.123.123'}]
        :param token: optional auth token.

        :raises: FailedToUpdateDHCPOptOnPort
        """
        port_req_body = {'port': {'extra_dhcp_opts': dhcp_options}}
        try:
            _build_client(token).update_port(port_id, port_req_body)
        except neutron_client_exc.NeutronClientException:
            LOG.exception(_LE("Failed to update Neutron port %s."), port_id)
            raise exception.FailedToUpdateDHCPOptOnPort(port_id=port_id)
Пример #23
0
def dump_sdr(task, file_path):
    """Dump SDR data to a file.

    :param task: a TaskManager instance.
    :param file_path: the path to SDR dump file.
    :raises: IPMIFailure on an error from ipmitool.
    :raises: MissingParameterValue if a required parameter is missing.
    :raises: InvalidParameterValue when an invalid value is specified.

    """
    node_uuid = task.node.uuid
    LOG.debug('Dump SDR data for node %(node)s to file %(name)s',
              {'name': file_path, 'node': node_uuid})
    driver_info = _parse_driver_info(task.node)
    cmd = 'sdr dump %s' % file_path

    try:
        out, err = _exec_ipmitool(driver_info, cmd)
        LOG.debug('dump SDR returned stdout: %(stdout)s, stderr:'
                  ' %(stderr)s', {'stdout': out, 'stderr': err})
    except (exception.PasswordFileFailedToCreate,
            processutils.ProcessExecutionError) as e:
        LOG.exception(_LE('IPMI "sdr dump" failed for node %(node_id)s '
                      'with error: %(error)s.'),
                      {'node_id': node_uuid, 'error': e})
        raise exception.IPMIFailure(cmd=cmd)
Пример #24
0
def finish_deploy(task, address):
    """Notifies the ramdisk to reboot the node and makes the instance active.

    This method notifies the ramdisk to proceed to reboot and then
    makes the instance active.

    :param task: a TaskManager object.
    :param address: The IP address of the bare metal node.
    :raises: InstanceDeployFailure, if notifying ramdisk failed.
    """
    node = task.node
    try:
        deploy_utils.notify_ramdisk_to_proceed(address)
    except Exception as e:
        LOG.error(
            _LE("Deploy failed for instance %(instance)s. " "Error: %(error)s"),
            {"instance": node.instance_uuid, "error": e},
        )
        msg = _("Failed to notify ramdisk to reboot after bootloader " "installation. Error: %s") % e
        deploy_utils.set_failed_state(task, msg)
        raise exception.InstanceDeployFailure(msg)

    # TODO(lucasagomes): When deploying a node with the DIB ramdisk
    # Ironic will not power control the node at the end of the deployment,
    # it's the DIB ramdisk that reboots the node. But, for the SSH driver
    # some changes like setting the boot device only gets applied when the
    # machine is powered off and on again. So the code below is enforcing
    # it. For Liberty we need to change the DIB ramdisk so that Ironic
    # always controls the power state of the node for all drivers.
    if deploy_utils.get_boot_option(node) == "local" and "ssh" in node.driver:
        manager_utils.node_power_action(task, states.REBOOT)

    LOG.info(_LI("Deployment to node %s done"), node.uuid)
    task.process_event("done")
Пример #25
0
def send_raw(task, raw_bytes):
    """Send raw bytes to the BMC. Bytes should be a string of bytes.

    :param task: a TaskManager instance.
    :param raw_bytes: a string of raw bytes to send, e.g. '0x00 0x01'
    :returns: a tuple with stdout and stderr.
    :raises: IPMIFailure on an error from ipmitool.
    :raises: MissingParameterValue if a required parameter is missing.
    :raises: InvalidParameterValue when an invalid value is specified.

    """
    node_uuid = task.node.uuid
    LOG.debug('Sending node %(node)s raw bytes %(bytes)s',
              {'bytes': raw_bytes, 'node': node_uuid})
    driver_info = _parse_driver_info(task.node)
    cmd = 'raw %s' % raw_bytes

    try:
        out, err = _exec_ipmitool(driver_info, cmd)
        LOG.debug('send raw bytes returned stdout: %(stdout)s, stderr:'
                  ' %(stderr)s', {'stdout': out, 'stderr': err})
    except (exception.PasswordFileFailedToCreate,
            processutils.ProcessExecutionError) as e:
        LOG.exception(_LE('IPMI "raw bytes" failed for node %(node_id)s '
                      'with error: %(error)s.'),
                      {'node_id': node_uuid, 'error': e})
        raise exception.IPMIFailure(cmd=cmd)

    return out, err
Пример #26
0
    def continue_deploy(self, task, **kwargs):
        """Method invoked when deployed with the IPA ramdisk.

        This method is invoked during a heartbeat from an agent when
        the node is in wait-call-back state. This deploys the image on
        the node and then configures the node to boot according to the
        desired boot option (netboot or localboot).

        :param task: a TaskManager object containing the node.
        :param kwargs: the kwargs passed from the heartbeat method.
        :raises: InstanceDeployFailure, if it encounters some error during
            the deploy.
        """
        task.process_event("resume")
        node = task.node
        LOG.debug("Continuing the deployment on node %s", node.uuid)

        uuid_dict_returned = do_agent_iscsi_deploy(task, self._client)

        if deploy_utils.get_boot_option(node) == "local":
            # Install the boot loader
            root_uuid = uuid_dict_returned.get("root uuid")
            efi_sys_uuid = uuid_dict_returned.get("efi system partition uuid")
            self.configure_local_boot(task, root_uuid=root_uuid, efi_system_part_uuid=efi_sys_uuid)

        try:
            task.driver.boot.prepare_instance(task)
        except Exception as e:
            LOG.error(
                _LE("Deploy failed for instance %(instance)s. " "Error: %(error)s"),
                {"instance": node.instance_uuid, "error": e},
            )
            msg = _("Failed to continue agent deployment.")
            deploy_utils.set_failed_state(task, msg)
        self.reboot_and_finish_deploy(task)
Пример #27
0
def set_failed_state(task, msg):
    """Sets the deploy status as failed with relevant messages.

    This method sets the deployment as fail with the given message.
    It sets node's provision_state to DEPLOYFAIL and updates last_error
    with the given error message. It also powers off the baremetal node.

    :param task: a TaskManager instance containing the node to act on.
    :param msg: the message to set in last_error of the node.
    """
    node = task.node
    node.provision_state = states.DEPLOYFAIL
    node.target_provision_state = states.NOSTATE
    node.save()
    try:
        manager_utils.node_power_action(task, states.POWER_OFF)
    except Exception:
        msg2 = (_LE('Node %s failed to power off while handling deploy '
                    'failure. This may be a serious condition. Node '
                    'should be removed from Ironic or put in maintenance '
                    'mode until the problem is resolved.') % node.uuid)
        LOG.exception(msg2)
    finally:
        # NOTE(deva): node_power_action() erases node.last_error
        #             so we need to set it again here.
        node.last_error = msg
        node.save()
Пример #28
0
    def set_boot_device(self, task, device, persistent=False):
        """Set the boot device for the task's node.

        Set the boot device to use on next reboot of the node.

        :param task: a task from TaskManager.
        :param device: the boot device, one of
                       :mod:`ironic.common.boot_devices`.
        :param persistent: Boolean value. True if the boot device will
                           persist to all future boots, False if not.
                           Default: False. Ignored by this driver.
        :raises: InvalidParameterValue if an invalid boot device is
                 specified or if a seamicro parameter is invalid.
        :raises: IronicException on an error from seamicro-client.
        :raises: MissingParameterValue when a required parameter is missing

        """
        if device not in self.get_supported_boot_devices():
            raise exception.InvalidParameterValue(_(
                "Invalid boot device %s specified.") % device)

        seamicro_info = _parse_driver_info(task.node)
        try:
            server = _get_server(seamicro_info)
            boot_device = _BOOT_DEVICES_MAP[device]
            server.set_boot_order(boot_device)
        except seamicro_client_exception.ClientException as ex:
            LOG.error(_LE("Seamicro set boot device failed for node "
                          "%(node)s with the following error: %(error)s"),
                      {'node': task.node.uuid, 'error': ex.message})
            raise exception.IronicException(message=ex.message)
Пример #29
0
def cleanup_vmedia_boot(task):
    """Cleans a node after a virtual media boot.

    This method cleans up a node after a virtual media boot. It deletes the
    floppy image if it exists in CONF.ilo.swift_ilo_container or web server.
    It also ejects both virtual media cdrom and virtual media floppy.

    :param task: a TaskManager instance containing the node to act on.
    """
    LOG.debug("Cleaning up node %s after virtual media boot", task.node.uuid)

    if not CONF.ilo.use_web_server_for_images:
        container = CONF.ilo.swift_ilo_container
        object_name = _get_floppy_image_name(task.node)
        try:
            swift_api = swift.SwiftAPI()
            swift_api.delete_object(container, object_name)
        except exception.SwiftObjectNotFoundError as e:
            LOG.warning(_LW("Temporary object associated with virtual floppy "
                            "was already deleted from Swift. Error: %s"), e)
        except exception.SwiftOperationError as e:
            LOG.exception(_LE("Error while deleting temporary swift object "
                              "%(object_name)s from %(container)s associated "
                              "with virtual floppy. Error: %(error)s"),
                          {'object_name': object_name, 'container': container,
                           'error': e})
    else:
        destroy_floppy_image_from_web_server(task.node)
    eject_vmedia_devices(task)
Пример #30
0
def _create_config_job(node):
    """Create a configuration job.

    This method is used to apply the pending values created by
    set_boot_device().

    :param node: an ironic node object.
    :raises: DracClientError if the client received unexpected response.
    :raises: DracOperationFailed if the client received response with an
             error message.
    :raises: DracUnexpectedReturnValue if the client received a response
             with unexpected return value.
    """
    client = drac_client.get_wsman_client(node)
    selectors = {'CreationClassName': 'DCIM_BIOSService',
                 'Name': 'DCIM:BIOSService',
                 'SystemCreationClassName': 'DCIM_ComputerSystem',
                 'SystemName': 'DCIM:ComputerSystem'}
    properties = {'Target': TARGET_DEVICE,
                  'ScheduledStartTime': 'TIME_NOW'}
    try:
        client.wsman_invoke(resource_uris.DCIM_BIOSService,
                            'CreateTargetedConfigJob', selectors, properties,
                            drac_client.RET_CREATED)
    except exception.DracRequestFailed as exc:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE('DRAC driver failed to create config job for node '
                          '%(node_uuid)s. The changes are not applied. '
                          'Reason: %(error)s.'),
                      {'node_uuid': node.uuid, 'error': exc})
Пример #31
0
def _get_boot_device_for_boot_list(node, boot_list_id, controller_version):
    """Get the next boot device for a given boot list.

    The DCIM_BootConfigSetting resource represents each boot list (eg.
    IPL/BIOS, BCV, UEFI, vFlash Partition, One Time Boot).
    The DCIM_BootSourceSetting resource represents each of the boot list boot
    devices or sources that are shown under their corresponding boot list.

    :param node: ironic node object.
    :param boot_list_id: boot list id.
    :param controller_version: version of the Lifecycle controller.
    :raises: DracClientError on an error from pywsman library.
    :returns: boot device id.
    """
    client = drac_client.get_wsman_client(node)

    if controller_version < '2.0.0':
        filter_query = ('select * from DCIM_BootSourceSetting where '
                        'PendingAssignedSequence=0')
    else:
        filter_query = ('select * from DCIM_BootSourceSetting where '
                        'PendingAssignedSequence=0 and '
                        'BootSourceType="%s"' % boot_list_id)
    try:
        doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting,
                                     filter_query=filter_query)
    except exception.DracClientError as exc:
        with excutils.save_and_reraise_exception():
            LOG.error(_LE('DRAC driver failed to get the current boot '
                          'device for node %(node_uuid)s. '
                          'Reason: %(error)s.'),
                      {'node_uuid': node.uuid, 'error': exc})

    if controller_version < '2.0.0':
        boot_devices = drac_common.find_xml(
            doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting,
            find_all=True)
        for device in boot_devices:
            if device.text.startswith(boot_list_id):
                boot_device_id = device.text
                break
    else:
        boot_device_id = drac_common.find_xml(
            doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text

    return boot_device_id
Пример #32
0
 def wrapper(*args, **kwargs):
     try:
         return f(*args, **kwargs)
     except kaexception.EndpointNotFound:
         service_type = kwargs.get('service_type', 'baremetal')
         endpoint_type = kwargs.get('endpoint_type', 'internal')
         raise exception.CatalogNotFound(
             service_type=service_type, endpoint_type=endpoint_type)
     except (kaexception.Unauthorized, kaexception.AuthorizationFailure):
         raise exception.KeystoneUnauthorized()
     except (kaexception.NoMatchingPlugin,
             kaexception.MissingRequiredOptions) as e:
         raise exception.ConfigInvalid(six.text_type(e))
     except Exception as e:
         LOG.exception(_LE('Keystone request failed: %(msg)s'),
                       {'msg': six.text_type(e)})
         raise exception.KeystoneFailure(six.text_type(e))
Пример #33
0
 def _check_request_status(self, response):
     repeat = False
     status = response.status_code
     response_json = response.json()
     if status in (409, ):
         ignored_conflicts = {'RABBITMQ_CLIENTCERT_CONFLICT'}
         if (response_json.get('errorCode') in ignored_conflicts):
             repeat = False
         else:
             repeat = True
         LOG.debug("Conflict contacting OneView: ", response_json)
     elif status in (404, 500):
         LOG.error(_LE("Error contacting OneView: "), response_json)
         LOG.error(("Error contacting OneView: "), response_json)
     elif status not in (200, 202):
         LOG.warn("Status not recognized:", status, response_json)
     return repeat
Пример #34
0
def _clean_up_boot_iso_for_instance(node):
    """Deletes the boot ISO created in Swift for the instance.

    :param node: an ironic node object.
    """
    swift_api = swift.SwiftAPI()
    container = CONF.ilo.swift_ilo_container
    boot_iso_object_name = _get_boot_iso_object_name(node)
    try:
        swift_api.delete_object(container, boot_iso_object_name)
    except exception.SwiftOperationError as e:
        LOG.exception(
            _LE("Failed to clean up boot ISO for %(node)s."
                "Error: %(error)s."), {
                    'node': node.uuid,
                    'error': e
                })
Пример #35
0
def _ensure_exception_kwargs_serializable(exc_class_name, kwargs):
    """Ensure that kwargs are serializable

    Ensure that all kwargs passed to exception constructor can be passed over
    RPC, by trying to convert them to JSON, or, as a last resort, to string.
    If it is not possible, unserializable kwargs will be removed, letting the
    receiver to handle the exception string as it is configured to.

    :param exc_class_name: an IronicException class name.
    :param kwargs: a dictionary of keyword arguments passed to the exception
        constructor.
    :returns: a dictionary of serializable keyword arguments.
    """
    serializers = [(jsonutils.dumps, _('when converting to JSON')),
                   (six.text_type, _('when converting to string'))]
    exceptions = collections.defaultdict(list)
    serializable_kwargs = {}
    for k, v in kwargs.items():
        for serializer, msg in serializers:
            try:
                serializable_kwargs[k] = serializer(v)
                exceptions.pop(k, None)
                break
            except Exception as e:
                exceptions[k].append(
                    '(%(serializer_type)s) %(e_type)s: %(e_contents)s' % {
                        'serializer_type': msg,
                        'e_contents': e,
                        'e_type': e.__class__.__name__
                    })
    if exceptions:
        LOG.error(
            _LE("One or more arguments passed to the %(exc_class)s "
                "constructor as kwargs can not be serialized. The serialized "
                "arguments: %(serialized)s. These unserialized kwargs were "
                "dropped because of the exceptions encountered during their "
                "serialization:\n%(errors)s"),
            dict(errors=';\n'.join("%s: %s" % (k, '; '.join(v))
                                   for k, v in exceptions.items()),
                 exc_class=exc_class_name,
                 serialized=serializable_kwargs))
        # We might be able to actually put the following keys' values into
        # format string, but there is no guarantee, drop it just in case.
        for k in exceptions:
            del kwargs[k]
    return serializable_kwargs
Пример #36
0
def _get_config(node, resource):
    """Helper for get_config.

    Handles getting BIOS config values for a single namespace

    :param node: an ironic node object.
    :param resource: the namespace.
    :returns: a dictionary that maps the name of each attribute to a dictionary
              of values of that attribute.
    :raises: InvalidParameterValue if some information required to connnect
             to the DRAC is missing on the node or the value of one or more
             required parameters is invalid.
    :raises: DracClientError on an error from pywsman library.
    :raises: DracOperationFailed if the specified resource is unknown.
    """
    res = {}
    client = wsman_client.get_wsman_client(node)
    try:
        doc = client.wsman_enumerate(resource)
    except exception.DracClientError as exc:
        with excutils.save_and_reraise_exception():
            LOG.error(
                _LE('DRAC driver failed to get BIOS settings '
                    'for resource %(resource)s '
                    'from node %(node_uuid)s. '
                    'Reason: %(error)s.'), {
                        'node_uuid': node.uuid,
                        'resource': resource,
                        'error': exc
                    })
    items = doc.find('.//{%s}Items' % resource_uris.CIM_WSMAN)
    for item in items:
        if resource == resource_uris.DCIM_BIOSEnumeration:
            attribute = parse_enumeration(item, resource)
        elif resource == resource_uris.DCIM_BIOSString:
            attribute = parse_string(item, resource)
        elif resource == resource_uris.DCIM_BIOSInteger:
            attribute = parse_integer(item, resource)
        else:
            raise exception.DracOperationFailed(
                message=_('Unknown namespace %(ns)s for item: "%(item)s"') % {
                    'item': ET.tostring(item),
                    'ns': resource
                })
        res[attribute['name']] = attribute
    return res
Пример #37
0
def rollback_ports(task, network_uuid):
    """Attempts to delete any ports created by cleaning/provisioning

    Purposefully will not raise any exceptions so error handling can
    continue.

    :param task: a TaskManager instance.
    :param network_uuid: UUID of a neutron network.
    """
    try:
        remove_ports_from_network(task, network_uuid)
    except exception.NetworkError:
        # Only log the error
        LOG.exception(_LE(
            'Failed to rollback port changes for node %(node)s '
            'on network %(network)s'), {'node': task.node.uuid,
                                        'network': network_uuid})
Пример #38
0
    def validate_image_url(url, secret=False):
        """Validates image URL through the HEAD request.

        :param url: URL to be validated
        :param secret: if URL is secret (e.g. swift temp url),
            it will not be shown in logs.
        """
        try:
            image_service.HttpImageService().validate_href(url, secret)
        except exception.ImageRefValidationFailed as e:
            with excutils.save_and_reraise_exception():
                LOG.error(_LE("Agent deploy supports only HTTP(S) URLs as "
                              "instance_info['image_source'] or swift "
                              "temporary URL. Either the specified URL is not "
                              "a valid HTTP(S) URL or is not reachable "
                              "for node %(node)s. Error: %(msg)s"),
                          {'node': node.uuid, 'msg': e})
Пример #39
0
    def validate(self, task):
        """Validate the deployment information for the task's node.

        :param task: a TaskManager instance containing the node to act on.
        :raises: InvalidParameterValue.
        :raises: MissingParameterValue
        """
        node = task.node

        # Check the boot_mode and boot_option capabilities values.
        driver_utils.validate_boot_mode_capability(node)
        driver_utils.validate_boot_option_capability(node)

        boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)

        if CONF.pxe.ipxe_enabled:
            if not CONF.pxe.http_url or not CONF.pxe.http_root:
                raise exception.MissingParameterValue(
                    _("iPXE boot is enabled but no HTTP URL or HTTP "
                      "root was specified."))
            # iPXE and UEFI should not be configured together.
            if boot_mode == 'uefi':
                LOG.error(
                    _LE("UEFI boot mode is not supported with "
                        "iPXE boot enabled."))
                raise exception.InvalidParameterValue(
                    _("Conflict: iPXE is enabled, but cannot be used with node"
                      "%(node_uuid)s configured to use UEFI boot") %
                    {'node_uuid': node.uuid})

        # Check if 'boot_option' is compatible with 'boot_mode' of uefi and
        # image being deployed
        validate_boot_option_for_uefi(task.node)

        d_info = _parse_deploy_info(node)

        iscsi_deploy.validate(task)

        if node.driver_internal_info.get('is_whole_disk_image'):
            props = []
        elif service_utils.is_glance_image(d_info['image_source']):
            props = ['kernel_id', 'ramdisk_id']
        else:
            props = ['kernel', 'ramdisk']

        iscsi_deploy.validate_image_properties(task.context, d_info, props)
Пример #40
0
    def heartbeat(self, task, **kwargs):
        """Method for agent to periodically check in.

        The agent should be sending its agent_url (so Ironic can talk back)
        as a kwarg. kwargs should have the following format::

         {
             'agent_url': 'http://AGENT_HOST:AGENT_PORT'
         }

        AGENT_PORT defaults to 9999.
        """
        node = task.node
        driver_info = node.driver_info
        LOG.debug(
            'Heartbeat from %(node)s, last heartbeat at %(heartbeat)s.',
            {'node': node.uuid,
             'heartbeat': driver_info.get('agent_last_heartbeat')})
        driver_info['agent_last_heartbeat'] = int(_time())
        try:
            driver_info['agent_url'] = kwargs['agent_url']
        except KeyError:
            raise exception.MissingParameterValue(_('For heartbeat operation, '
                                                    '"agent_url" must be '
                                                    'specified.'))

        node.driver_info = driver_info
        node.save()

        # Async call backs don't set error state on their own
        # TODO(jimrollenhagen) improve error messages here
        msg = _('Failed checking if deploy is done.')
        try:
            if node.provision_state == states.DEPLOYWAIT:
                msg = _('Node failed to get image for deploy.')
                self._continue_deploy(task, **kwargs)
            elif (node.provision_state == states.DEPLOYING
                    and self._deploy_is_done(node)):
                msg = _('Node failed to move to active state.')
                self._reboot_to_instance(task, **kwargs)
        except Exception:
            LOG.exception(_LE('Async exception for %(node)s: %(msg)s'),
                          {'node': node,
                           'msg': msg})
            deploy_utils.set_failed_state(task, msg)
Пример #41
0
    def get_boot_device(self, task):
        """Get the current boot device for the task's node.

        Returns the current boot device of the node.

        :param task: a task from TaskManager.
        :raises: MissingParameterValue if required IPMI parameters
            are missing.
        :raises: IPMIFailure on an error from pyghmi.
        :returns: a dictionary containing:

            :boot_device: the boot device, one of
                :mod:`ironic.common.boot_devices` or None if it is unknown.
            :persistent: Whether the boot device will persist to all
                future boots or not, None if it is unknown.

        """
        driver_info = _parse_driver_info(task.node)
        response = {'boot_device': None}
        try:
            ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
                                           userid=driver_info['username'],
                                           password=driver_info['password'])
            ret = ipmicmd.get_bootdev()
            # FIXME(lucasagomes): pyghmi doesn't seem to handle errors
            # consistently, for some errors it raises an exception
            # others it just returns a dictionary with the error.
            if 'error' in ret:
                raise pyghmi_exception.IpmiException(ret['error'])
        except pyghmi_exception.IpmiException as e:
            LOG.error(
                _LE("IPMI get boot device failed for node %(node_id)s "
                    "with the following error: %(error)s"), {
                        'node_id': driver_info['uuid'],
                        'error': e
                    })
            raise exception.IPMIFailure(cmd=e)

        response['persistent'] = ret.get('persistent')
        bootdev = ret.get('bootdev')
        if bootdev:
            response['boot_device'] = next(
                (dev for dev, hdev in _BOOT_DEVICES_MAP.items()
                 if hdev == bootdev), None)
        return response
Пример #42
0
def _get_power_status(ssh_obj, driver_info):
    """Returns a node's current power state.

    :param ssh_obj: paramiko.SSHClient, an active ssh connection.
    :param driver_info: information for accessing the node.
    :returns: one of ironic.common.states POWER_OFF, POWER_ON.
    :raises: NodeNotFound

    """
    power_state = None
    node_name = _get_hosts_name_for_node(ssh_obj, driver_info)
    if node_name:
        # Get a list of vms running on the host. If the command supports
        # it, explicitly specify the desired node."
        cmd_to_exec = "%s %s" % (driver_info['cmd_set']['base_cmd'],
                                 driver_info['cmd_set']['list_running'])
        cmd_to_exec = cmd_to_exec.replace('{_NodeName_}', node_name)
        running_list = _ssh_execute(ssh_obj, cmd_to_exec)

        # Command should return a list of running vms. If the current node is
        # not listed then we can assume it is not powered on.
        quoted_node_name = '"%s"' % node_name
        for node in running_list:
            if not node:
                continue
            # 'node' here is an formatted output from the virt cli's. The
            # node name is always quoted but can contain other information.
            # vbox returns '"NodeName" {b43c4982-110c-4c29-9325-d5f41b053513}'
            # so we must use the 'in' comparison here and not '=='
            if quoted_node_name in node:
                power_state = states.POWER_ON
                break
        if not power_state:
            power_state = states.POWER_OFF
    else:
        err_msg = _LE('Node "%(host)s" with MAC address %(mac)s not found.')
        LOG.error(err_msg, {
            'host': driver_info['host'],
            'mac': driver_info['macs']
        })

        raise exception.NodeNotFound(node=driver_info['host'])

    return power_state
Пример #43
0
    def set_boot_device(self, task, device, persistent=False):
        """Set the boot device for the task's node.

        Set the boot device to use on next reboot of the node.

        :param task: a task from TaskManager.
        :param device: the boot device, one of
                       :mod:`ironic.common.boot_devices`.
        :param persistent: Boolean value. True if the boot device will
                           persist to all future boots, False if not.
                           Default: False.
        :raises: InvalidParameterValue if an invalid boot device is specified
                 or required ipmi credentials are missing.
        :raises: MissingParameterValue when required ipmi credentials
                 are missing.
        :raises: IPMIFailure on an error from pyghmi.
        """
        if device not in self.get_supported_boot_devices(task):
            raise exception.InvalidParameterValue(
                _("Invalid boot device %s specified.") % device)

        if task.node.driver_info.get('ipmi_force_boot_device', False):
            driver_utils.force_persistent_boot(task, device, persistent)
            # Reset persistent to False, in case of BMC does not support
            # persistent or we do not have admin rights.
            persistent = False

        boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node)
        driver_info = _parse_driver_info(task.node)
        try:
            ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
                                           userid=driver_info['username'],
                                           password=driver_info['password'])
            bootdev = _BOOT_DEVICES_MAP[device]
            uefiboot = boot_mode == 'uefi'
            ipmicmd.set_bootdev(bootdev, persist=persistent, uefiboot=uefiboot)
        except pyghmi_exception.IpmiException as e:
            LOG.error(
                _LE("IPMI set boot device failed for node %(node_id)s "
                    "with the following error: %(error)s"), {
                        'node_id': driver_info['uuid'],
                        'error': e
                    })
            raise exception.IPMIFailure(cmd=e)
Пример #44
0
def set_boot_device(node, device, persistent=False):
    """Set the boot device for a node.

    Set the boot device to use on next boot of the node.

    :param node: an ironic node object.
    :param device: the boot device, one of
                   :mod:`ironic.common.boot_devices`.
    :param persistent: Boolean value. True if the boot device will
                       persist to all future boots, False if not.
                       Default: False.
    :raises: DracOperationError on an error from python-dracclient.
    """

    drac_job.validate_job_queue(node)

    client = drac_common.get_drac_client(node)

    try:
        drac_boot_devices = client.list_boot_devices()

        current_boot_device = _get_boot_device(node, drac_boot_devices)
        # If we are already booting from the right device, do nothing.
        if current_boot_device == {'boot_device': device,
                                   'persistent': persistent}:
            LOG.debug('DRAC already set to boot from %s', device)
            return

        drac_boot_device = next(drac_device.id for drac_device
                                in drac_boot_devices[PERSISTENT_BOOT_MODE]
                                if _BOOT_DEVICES_MAP[device] in drac_device.id)

        if persistent:
            boot_list = PERSISTENT_BOOT_MODE
        else:
            boot_list = NON_PERSISTENT_BOOT_MODE

        client.change_boot_device_order(boot_list, drac_boot_device)
        client.commit_pending_bios_changes()
    except drac_exceptions.BaseClientException as exc:
        LOG.error(_LE('DRAC driver failed to change boot device order for '
                      'node %(node_uuid)s. Reason: %(error)s.'),
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)
Пример #45
0
    def get_boot_device(self, task):
        """Get the current boot device for a node.

        Returns the current boot device of the node.

        :param task: a task from TaskManager.
        :raises: DracClientError on an error from pywsman library.
        :returns: a dictionary containing:

            :boot_device: the boot device, one of
                :mod:`ironic.common.boot_devices` or None if it is unknown.
            :persistent: Whether the boot device will persist to all
                future boots or not, None if it is unknown.

        """
        client = drac_common.get_wsman_client(task.node)
        boot_mode = _get_next_boot_mode(task.node)

        persistent = boot_mode['is_next'] == PERSISTENT
        instance_id = boot_mode['instance_id']

        options = pywsman.ClientOptions()
        filter_query = ('select * from DCIM_BootSourceSetting where '
                        'PendingAssignedSequence=0 and '
                        'BootSourceType="%s"' % instance_id)
        try:
            doc = client.wsman_enumerate(resource_uris.DCIM_BootSourceSetting,
                                         options,
                                         filter_query=filter_query)
        except exception.DracClientError as exc:
            with excutils.save_and_reraise_exception():
                LOG.error(
                    _LE('DRAC driver failed to get the current boot '
                        'device for node %(node_uuid)s. '
                        'Reason: %(error)s.'), {
                            'node_uuid': task.node.uuid,
                            'error': exc
                        })

        instance_id = drac_common.find_xml(
            doc, 'InstanceID', resource_uris.DCIM_BootSourceSetting).text
        boot_device = next((key for (key, value) in _BOOT_DEVICES_MAP.items()
                            if value in instance_id), None)
        return {'boot_device': boot_device, 'persistent': persistent}
Пример #46
0
def create_virtual_disk(node,
                        raid_controller,
                        physical_disks,
                        raid_level,
                        size_mb,
                        disk_name=None,
                        span_length=None,
                        span_depth=None):
    """Create a single virtual disk on a RAID controller.

    The created virtual disk will be in pending state. The DRAC card will do
    the actual configuration once the changes are applied by calling the
    ``commit_config`` method.

    :param node: an ironic node object.
    :param raid_controller: id of the RAID controller.
    :param physical_disks: ids of the physical disks.
    :param raid_level: RAID level of the virtual disk.
    :param size_mb: size of the virtual disk.
    :param disk_name: name of the virtual disk. (optional)
    :param span_depth: Number of spans in virtual disk. (optional)
    :param span_length: Number of disks per span. (optional)
    :returns: a dictionary containing the commit_needed key with a boolean
              value indicating whether a config job must be created for the
              values to be applied.
    :raises: DracOperationError on an error from python-dracclient.
    """
    drac_job.validate_job_queue(node)

    client = drac_common.get_drac_client(node)

    try:
        return client.create_virtual_disk(raid_controller, physical_disks,
                                          raid_level, size_mb, disk_name,
                                          span_length, span_depth)
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            _LE('DRAC driver failed to create virtual disk for node '
                '%(node_uuid)s. Reason: %(error)s.'), {
                    'node_uuid': node.uuid,
                    'error': exc
                })
        raise exception.DracOperationError(error=exc)
Пример #47
0
def list_unfinished_jobs(node):
    """List unfinished config jobs of the node.

    :param node: an ironic node object.
    :returns: a list of Job objects from dracclient.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.list_jobs(only_unfinished=True)
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            _LE('DRAC driver failed to get the list of unfinished jobs '
                'for node %(node_uuid)s. Reason: %(error)s.'), {
                    'node_uuid': node.uuid,
                    'error': exc
                })
        raise exception.DracOperationError(error=exc)
Пример #48
0
def _start_inspection(node_uuid, context):
    """Call to inspector to start inspection."""
    context.ensure_thread_contain_context()
    try:
        _call_inspector(client.introspect, node_uuid, context)
    except Exception as exc:
        LOG.exception(_LE('Exception during contacting ironic-inspector '
                          'for inspection of node %(node)s: %(err)s'),
                      {'node': node_uuid, 'err': exc})
        # NOTE(dtantsur): if acquire fails our last option is to rely on
        # timeout
        lock_purpose = 'recording hardware inspection error'
        with task_manager.acquire(context, node_uuid,
                                  purpose=lock_purpose) as task:
            task.node.last_error = _('Failed to start inspection: %s') % exc
            task.process_event('fail')
    else:
        LOG.info(_LI('Node %s was sent to inspection to ironic-inspector'),
                 node_uuid)
Пример #49
0
    def bmc_reset(self, task, http_method, warm=True):
        """Reset BMC with IPMI command 'bmc reset (warm|cold)'.

        :param task: a TaskManager instance.
        :param http_method: the HTTP method used on the request.
        :param warm: boolean parameter to decide on warm or cold reset.
        :raises: IPMIFailure on an error from ipmitool.
        :raises: MissingParameterValue if a required parameter is missing.
        :raises: InvalidParameterValue when an invalid value is specified

        """
        node_uuid = task.node.uuid

        warm = strutils.bool_from_string(warm)
        if warm:
            warm_param = 'warm'
        else:
            warm_param = 'cold'

        LOG.debug('Doing %(warm)s BMC reset on node %(node)s', {
            'warm': warm_param,
            'node': node_uuid
        })
        driver_info = _parse_driver_info(task.node)
        cmd = 'bmc reset %s' % warm_param

        try:
            out, err = _exec_ipmitool(driver_info, cmd)
            LOG.debug(
                'bmc reset returned stdout: %(stdout)s, stderr:'
                ' %(stderr)s', {
                    'stdout': out,
                    'stderr': err
                })
        except (exception.PasswordFileFailedToCreate,
                processutils.ProcessExecutionError) as e:
            LOG.exception(
                _LE('IPMI "bmc reset" failed for node %(node_id)s '
                    'with error: %(error)s.'), {
                        'node_id': node_uuid,
                        'error': e
                    })
            raise exception.IPMIFailure(cmd=cmd)
Пример #50
0
def list_raid_controllers(node):
    """List the RAID controllers of the node.

    :param node: an ironic node object.
    :returns: a list of RAIDController objects from dracclient.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.list_raid_controllers()
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            _LE('DRAC driver failed to get the list of RAID controllers '
                'for node %(node_uuid)s. Reason: %(error)s.'), {
                    'node_uuid': node.uuid,
                    'error': exc
                })
        raise exception.DracOperationError(error=exc)
Пример #51
0
    def _exec_cmd(self, rel_url):
        """Executes a command by calling the chassis manager API."""
        url = posixpath.join(self._base_url, rel_url)
        try:
            response = requests.get(url,
                                    auth=auth.HTTPBasicAuth(
                                        self._username, self._password))
            response.raise_for_status()
        except requests_exceptions.RequestException as ex:
            LOG.exception(_LE("HTTP call failed: %s"), ex)
            raise exception.MSFTOCSClientApiException(
                _("HTTP call failed: %s") % ex.args[0])

        xml_response = response.text
        LOG.debug("Call to %(url)s got response: %(xml_response)s", {
            "url": url,
            "xml_response": xml_response
        })
        return xml_response
Пример #52
0
def abandon_config(task):
    """Abandons uncommitted changes added by set_config

    :param task: a TaskManager instance containing the node to act on.
    :raises: DracOperationError on an error from python-dracclient.
    """
    node = task.node
    client = drac_common.get_drac_client(node)

    try:
        client.abandon_pending_bios_changes()
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            _LE('DRAC driver failed to delete the pending BIOS '
                'settings for node %(node_uuid)s. Reason: %(error)s.'), {
                    'node_uuid': node.uuid,
                    'error': exc
                })
        raise exception.DracOperationError(error=exc)
Пример #53
0
def _start_inspection(node_uuid, context):
    """Call to discoverd to start inspection."""
    try:
        _call_discoverd(client.introspect, node_uuid, context)
    except Exception as exc:
        LOG.exception(
            _LE('Exception during contacting ironic-discoverd '
                'for inspection of node %(node)s: %(err)s'), {
                    'node': node_uuid,
                    'err': exc
                })
        # NOTE(dtantsur): if acquire fails our last option is to rely on
        # timeout
        with task_manager.acquire(context, node_uuid) as task:
            task.node.last_error = _('Failed to start inspection: %s') % exc
            task.process_event('fail')
    else:
        LOG.info(_LI('Node %s was sent to inspection to ironic-discoverd'),
                 node_uuid)
Пример #54
0
    def vendor_passthru(self, task, **kwargs):
        """A node that knows its UUID should heartbeat to this passthru.

        It will get its node object back, with what Ironic thinks its provision
        state is and the target provision state is.
        """
        method = kwargs['method']  # Existence checked in mixin
        if method not in self.vendor_routes:
            raise exception.InvalidParameterValue(
                _('No handler for method '
                  '%s') % method)
        func = self.vendor_routes[method]
        try:
            return func(task, **kwargs)
        except Exception:
            # catch-all in case something bubbles up here
            with excutils.save_and_reraise_exception():
                LOG.exception(_LE('vendor_passthru failed with method %s'),
                              method)
Пример #55
0
def _check_for_config_job(node):
    """Check if a configuration job is already created.

    :param node: an ironic node object.
    :raises: DracClientError on an error from pywsman library.
    :raises: DracPendingConfigJobExists if the job is already created.

    """
    client = drac_client.get_wsman_client(node)
    try:
        doc = client.wsman_enumerate(resource_uris.DCIM_LifecycleJob)
    except exception.DracClientError as exc:
        with excutils.save_and_reraise_exception():
            LOG.error(
                _LE('DRAC driver failed to list the configuration jobs '
                    'for node %(node_uuid)s. Reason: %(error)s.'), {
                        'node_uuid': node.uuid,
                        'error': exc
                    })

    items = drac_common.find_xml(doc,
                                 'DCIM_LifecycleJob',
                                 resource_uris.DCIM_LifecycleJob,
                                 find_all=True)
    for i in items:
        name = drac_common.find_xml(i, 'Name', resource_uris.DCIM_LifecycleJob)
        if TARGET_DEVICE not in name.text:
            continue

        job_status = drac_common.find_xml(i, 'JobStatus',
                                          resource_uris.DCIM_LifecycleJob).text
        # If job is already completed or failed we can
        # create another one.
        # The 'Completed with Errors' JobStatus can be returned by
        # configuration jobs that set NIC or BIOS attributes.
        # Job Control Documentation: http://goo.gl/o1dDD3 (Section 7.2.3.2)
        if job_status.lower() not in ('completed', 'completed with errors',
                                      'failed'):
            job_id = drac_common.find_xml(i, 'InstanceID',
                                          resource_uris.DCIM_LifecycleJob).text
            raise exception.DracPendingConfigJobExists(job_id=job_id,
                                                       target=TARGET_DEVICE)
Пример #56
0
def _check_for_config_job(node):
    """Check if a configuration job is already created.

    :param node: an ironic node object.
    :raises: DracClientError on an error from pywsman library.
    :raises: DracConfigJobCreationError if the job is already created.

    """
    client = drac_common.get_wsman_client(node)
    options = pywsman.ClientOptions()
    try:
        doc = client.wsman_enumerate(resource_uris.DCIM_LifecycleJob, options)
    except exception.DracClientError as exc:
        with excutils.save_and_reraise_exception():
            LOG.error(
                _LE('DRAC driver failed to list the configuration jobs '
                    'for node %(node_uuid)s. Reason: %(error)s.'), {
                        'node_uuid': node.uuid,
                        'error': exc
                    })

    items = drac_common.find_xml(doc,
                                 'DCIM_LifecycleJob',
                                 resource_uris.DCIM_LifecycleJob,
                                 find_all=True)
    for i in items:
        name = drac_common.find_xml(i, 'Name', resource_uris.DCIM_LifecycleJob)
        if 'BIOS.Setup.1-1' not in name.text:
            continue

        job_status = drac_common.find_xml(i, 'JobStatus',
                                          resource_uris.DCIM_LifecycleJob).text
        # If job is already completed or failed we can
        # create another one.
        # Job Control Documentation: http://goo.gl/o1dDD3 (Section 7.2.3.2)
        if job_status.lower() not in ('completed', 'failed'):
            job_id = drac_common.find_xml(i, 'InstanceID',
                                          resource_uris.DCIM_LifecycleJob).text
            reason = (_('Another job with ID "%s" is already created '
                        'to configure the BIOS. Wait until existing job '
                        'is completed or is cancelled') % job_id)
            raise exception.DracConfigJobCreationError(error=reason)
Пример #57
0
    def set_boot_device(self, task, device, persistent=False):
        """Set the boot device for the task's node.

        Set the boot device to use on next reboot of the node.

        :param task: a task from TaskManager.
        :param device: the boot device, one of
                       :mod:`ironic.common.boot_devices`.
        :param persistent: Boolean value. True if the boot device will
                           persist to all future boots, False if not.
                           Default: False. Ignored by this driver.
        :raises: InvalidParameterValue if an invalid boot device is
                 specified or if any connection parameters are incorrect.
        :raises: MissingParameterValue if a required parameter is missing
        :raises: SSHConnectFailed if ssh failed to connect to the node.
        :raises: SSHCommandFailed on an error from ssh.
        :raises: NotImplementedError if the virt_type does not support
            setting the boot device.
        :raises: NodeNotFound if could not find a VM corresponding to any
            of the provided MACs.

        """
        node = task.node
        driver_info = _parse_driver_info(node)
        if device not in self.get_supported_boot_devices(task):
            raise exception.InvalidParameterValue(
                _("Invalid boot device %s specified.") % device)
        driver_info['macs'] = driver_utils.get_node_mac_addresses(task)
        ssh_obj = _get_connection(node)

        boot_device_map = _get_boot_device_map(driver_info['virt_type'])
        try:
            _set_boot_device(ssh_obj, driver_info, boot_device_map[device])
        except NotImplementedError:
            with excutils.save_and_reraise_exception():
                LOG.error(
                    _LE("Failed to set boot device for node %(node)s, "
                        "virt_type %(vtype)s does not support this "
                        "operation"), {
                            'node': node.uuid,
                            'vtype': driver_info['virt_type']
                        })
Пример #58
0
    def execute_clean_step(self, task, step):
        """Execute a clean step.

        :param task: a TaskManager object containing the node
        :param step: a clean step dictionary to execute
        :returns: None
        """
        node = task.node
        playbook, user, key = _parse_ansible_driver_info(task.node,
                                                         action='clean')
        stepname = step['step']
        try:
            ip_addr = node.driver_internal_info['ansible_cleaning_ip']
        except KeyError:
            raise exception.NodeCleaningFailure(node=node.uuid,
                                                reason='undefined node IP '
                                                'addresses')
        node_list = [(node.uuid, ip_addr, user, node.extra)]
        extra_vars = _prepare_extra_vars(node_list)

        LOG.debug('Starting cleaning step %(step)s on node %(node)s', {
            'node': node.uuid,
            'step': stepname
        })
        step_tags = step['args'].get('tags', [])
        try:
            _run_playbook(playbook, extra_vars, key, tags=step_tags)
        except exception.InstanceDeployFailure as e:
            LOG.error(
                _LE("Ansible failed cleaning step %(step)s "
                    "on node %(node)s."), {
                        'node': node.uuid,
                        'step': stepname
                    })
            manager_utils.cleaning_error_handler(task, six.text_type(e))
        else:
            LOG.info(
                _LI('Ansible completed cleaning step %(step)s '
                    'on node %(node)s.'), {
                        'node': node.uuid,
                        'step': stepname
                    })
Пример #59
0
def cleaning_error_handler(task, msg, tear_down_cleaning=True,
                           set_fail_state=True):
    """Put a failed node in CLEANFAIL or ZAPFAIL and maintenance."""
    # Reset clean step, msg should include current step
    if task.node.provision_state in (states.CLEANING, states.CLEANWAIT):
        task.node.clean_step = {}
    task.node.last_error = msg
    task.node.maintenance = True
    task.node.maintenance_reason = msg
    task.node.save()
    if tear_down_cleaning:
        try:
            task.driver.deploy.tear_down_cleaning(task)
        except Exception as e:
            msg = (_LE('Failed to tear down cleaning on node %(uuid)s, '
                       'reason: %(err)s'), {'err': e, 'uuid': task.node.uuid})
            LOG.exception(msg)

    if set_fail_state:
        task.process_event('fail')
Пример #60
0
 def inner(self, *args, **kwargs):
     oneview_client = self.oneview_client
     task = args[0]
     oneview_info = get_oneview_info(task.node)
     try:
         node_has_server_profile = (
             oneview_client.get_server_profile_from_hardware(oneview_info))
     except oneview_exceptions.OneViewException as oneview_exc:
         LOG.error(
             _LE("Failed to get server profile from OneView appliance for"
                 " node %(node)s. Error: %(message)s"), {
                     "node": task.node.uuid,
                     "message": oneview_exc
                 })
         raise exception.OneViewError(error=oneview_exc)
     if not node_has_server_profile:
         raise exception.OperationNotPermitted(
             _("A Server Profile is not associated with node %s.") %
             task.node.uuid)
     return func(self, *args, **kwargs)