Example #1
0
def policy_deprecation_check():
    global CHECKED_DEPRECATED_POLICY_ARGS
    if not CHECKED_DEPRECATED_POLICY_ARGS:
        enforcer = policy.get_enforcer()
        substitution_dict = {
            'user': '******',
            'domain_id': 'user_domain_id',
            'domain_name': 'user_domain_id',
            'tenant': 'project_name',
        }
        policy_rules = enforcer.file_rules.values()
        for rule in policy_rules:
            str_rule = six.text_type(rule)
            for deprecated, replacement in substitution_dict.items():
                if re.search(r'\b%s\b' % deprecated, str_rule):
                    LOG.warning(_LW(
                        "Deprecated argument %(deprecated)s is used in policy "
                        "file rule (%(rule)s), please use %(replacement)s "
                        "argument instead. The possibility to use deprecated "
                        "arguments will be removed in the Pike release."),
                        {'deprecated': deprecated, 'replacement': replacement,
                         'rule': str_rule})
                    if deprecated == 'domain_name':
                        LOG.warning(_LW(
                            "Please note that user_domain_id is an ID of the "
                            "user domain, while the deprecated domain_name is "
                            "its name. The policy rule has to be updated "
                            "accordingly."))
        CHECKED_DEPRECATED_POLICY_ARGS = True
Example #2
0
File: pxe.py Project: uggla/ironic
    def prepare(self, task):
        """Prepare the deployment environment for this task's node.

        Generates the TFTP configuration for PXE-booting both the deployment
        and user images, fetches the TFTP image from Glance and add it to the
        local cache.

        :param task: a TaskManager instance containing the node to act on.
        """
        node = task.node
        # TODO(deva): optimize this if rerun on existing files
        if CONF.pxe.ipxe_enabled:
            # Copy the iPXE boot script to HTTP root directory
            bootfile_path = os.path.join(CONF.pxe.http_root,
                                   os.path.basename(CONF.pxe.ipxe_boot_script))
            shutil.copyfile(CONF.pxe.ipxe_boot_script, bootfile_path)
        pxe_info = _get_image_info(node, task.context)
        pxe_options = _build_pxe_config_options(node, pxe_info,
                                                task.context)

        if deploy_utils.get_boot_mode_for_deploy(node) == 'uefi':
            pxe_config_template = CONF.pxe.uefi_pxe_config_template
        else:
            pxe_config_template = CONF.pxe.pxe_config_template

        pxe_utils.create_pxe_config(task, pxe_options,
                                    pxe_config_template)

        # FIXME(lucasagomes): If it's local boot we should not cache
        # the image kernel and ramdisk (Or even require it).
        _cache_ramdisk_kernel(task.context, node, pxe_info)

        # NOTE(deva): prepare may be called from conductor._do_takeover
        # in which case, it may need to regenerate the PXE config file for an
        # already-active deployment.
        if node.provision_state == states.ACTIVE:
            # this should have been stashed when the deploy was done
            # but let's guard, just in case it's missing
            iwdi = node.driver_internal_info.get('is_whole_disk_image')
            try:
                root_uuid_or_disk_id = node.driver_internal_info[
                                                'root_uuid_or_disk_id']
            except KeyError:
                if not iwdi:
                    LOG.warn(_LW("The UUID for the root partition can't be "
                             "found, unable to switch the pxe config from "
                             "deployment mode to service (boot) mode for node "
                             "%(node)s"), {"node": node.uuid})
                else:
                    LOG.warn(_LW("The disk id for the whole disk image can't "
                             "be found, unable to switch the pxe config from "
                             "deployment mode to service (boot) mode for "
                             "node %(node)s"), {"node": node.uuid})
            else:
                pxe_config_path = pxe_utils.get_pxe_config_file_path(
                    node.uuid)
                deploy_utils.switch_pxe_config(
                    pxe_config_path, root_uuid_or_disk_id,
                    deploy_utils.get_boot_mode_for_deploy(node),
                    iwdi)
Example #3
0
def _power_status(driver_info):
    """Get the power status for this node.

    :param driver_info: the bmc access info for a node.
    :returns: power state POWER_ON, POWER_OFF or ERROR defined in
             :class:`ironic.common.states`.
    :raises: IPMIFailure when the native ipmi call fails.
    """

    try:
        ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
                           userid=driver_info['username'],
                           password=driver_info['password'])
        ret = ipmicmd.get_power()
    except pyghmi_exception.IpmiException as e:
        LOG.warning(_LW("IPMI get power state failed for node %(node_id)s "
                        "with the following error: %(error)s"),
                    {'node_id': driver_info['uuid'], 'error': str(e)})
        raise exception.IPMIFailure(cmd=str(e))

    state = ret.get('powerstate')
    if state == 'on':
        return states.POWER_ON
    elif state == 'off':
        return states.POWER_OFF
    else:
        # NOTE(linggao): Do not throw an exception here because it might
        # return other valid values. It is up to the caller to decide
        # what to do.
        LOG.warning(_LW("IPMI get power state for node %(node_id)s returns the"
                        " following details: %(detail)s"),
                    {'node_id': driver_info['uuid'], 'detail': ret})
        return states.ERROR
Example #4
0
    def _wait(status):
        status["power"] = _power_status(node)
        if status["power"] == target_state:
            raise loopingcall.LoopingCallDone()

        if status["iter"] >= CONF.amt.max_attempts:
            status["power"] = states.ERROR
            LOG.warning(
                _LW("AMT failed to set power state %(state)s after " "%(tries)s retries on node %(node_id)s."),
                {"state": target_state, "tries": status["iter"], "node_id": node.uuid},
            )
            raise loopingcall.LoopingCallDone()

        try:
            _set_power_state(node, target_state)
        except Exception:
            # Log failures but keep trying
            LOG.warning(
                _LW(
                    "AMT set power state %(state)s for node %(node)s "
                    "- Attempt %(attempt)s times of %(max_attempt)s "
                    "failed."
                ),
                {
                    "state": target_state,
                    "node": node.uuid,
                    "attempt": status["iter"] + 1,
                    "max_attempt": CONF.amt.max_attempts,
                },
            )
        status["iter"] += 1
Example #5
0
    def prepare_instance(self, task):
        """Prepares the boot of instance.

        This method prepares the boot of the instance after reading
        relevant information from the node's instance_info. In case of netboot,
        it updates the dhcp entries and switches the PXE config. In case of
        localboot, it cleans up the PXE config.

        :param task: a task from TaskManager.
        :returns: None
        """
        node = task.node
        boot_option = deploy_utils.get_boot_option(node)

        if boot_option != "local":
            # Make sure that the instance kernel/ramdisk is cached.
            # This is for the takeover scenario for active nodes.
            instance_image_info = _get_instance_image_info(
                task.node, task.context)
            _cache_ramdisk_kernel(task.context, task.node, instance_image_info)

            # If it's going to PXE boot we need to update the DHCP server
            dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
            provider = dhcp_factory.DHCPFactory()
            provider.update_dhcp(task, dhcp_opts)

            iwdi = task.node.driver_internal_info.get('is_whole_disk_image')
            try:
                root_uuid_or_disk_id = task.node.driver_internal_info[
                    'root_uuid_or_disk_id'
                ]
            except KeyError:
                if not iwdi:
                    LOG.warn(_LW("The UUID for the root partition can't be "
                                 "found, unable to switch the pxe config from "
                                 "deployment mode to service (boot) mode for "
                                 "node %(node)s"), {"node": task.node.uuid})
                else:
                    LOG.warn(_LW("The disk id for the whole disk image can't "
                                 "be found, unable to switch the pxe config "
                                 "from deployment mode to service (boot) mode "
                                 "for node %(node)s"),
                             {"node": task.node.uuid})
            else:
                pxe_config_path = pxe_utils.get_pxe_config_file_path(
                    task.node.uuid)
                deploy_utils.switch_pxe_config(
                    pxe_config_path, root_uuid_or_disk_id,
                    deploy_utils.get_boot_mode_for_deploy(node),
                    iwdi, deploy_utils.is_trusted_boot_requested(node))

        else:
            # If it's going to boot from the local disk, we don't need
            # PXE config files. They still need to be generated as part
            # of the prepare() because the deployment does PXE boot the
            # deploy ramdisk
            pxe_utils.clean_up_pxe_config(task)
Example #6
0
    def inspect_hardware(self, task):
        """Inspect hardware.

        Inspect hardware to obtain the essential hardware properties and
        mac addresses.

        :param task: a task from TaskManager.
        :raises: HardwareInspectionFailure, if hardware inspection failed.
        :returns: states.MANAGEABLE, if hardware inspection succeeded.
        """
        node = task.node
        (props, macs) = _inspect_hardware(node)
        node.properties = dict(node.properties, **props)
        node.save()

        for mac in macs:
            try:
                new_port = objects.Port(task.context,
                                        address=mac, node_id=node.id)
                new_port.create()
                LOG.info(_LI("Port created for MAC address %(address)s "
                             "for node %(node_uuid)s during inspection"),
                         {'address': mac, 'node_uuid': node.uuid})
            except exception.MACAlreadyExists:
                LOG.warning(_LW("Port already existed for MAC address "
                                "%(address)s for node %(node_uuid)s "
                                "during inspection"),
                            {'address': mac, 'node_uuid': node.uuid})

        LOG.info(_LI("Node %s inspected"), node.uuid)
        return states.MANAGEABLE
Example #7
0
def _stop_console(node_uuid):
    """Close the serial console for a node

    Kills the console process and deletes the PID file.

    :param node_uuid: the UUID of the node
    :raises: NoConsolePid if no console PID was found
    :raises: ConsoleError if unable to stop the console process
    """

    try:
        console_pid = _get_console_pid(node_uuid)

        os.kill(console_pid, signal.SIGTERM)
    except OSError as exc:
        if exc.errno != errno.ESRCH:
            msg = (_("Could not stop the console for node '%(node)s'. "
                     "Reason: %(err)s.") % {'node': node_uuid, 'err': exc})
            raise exception.ConsoleError(message=msg)
        else:
            LOG.warning(_LW("Console process for node %s is not running "
                            "but pid file exists while trying to stop "
                            "shellinabox console."), node_uuid)
    finally:
        ironic_utils.unlink_without_raise(_get_console_pid_file(node_uuid))
Example #8
0
def _delete_master_path_if_stale(master_path, href, ctx):
    """Delete image from cache if it is not up to date with href contents.

    :param master_path: path to an image in master cache
    :param href: image href
    :param ctx: context to use
    :returns: True if master_path is up to date with href contents,
        False if master_path was stale and was deleted or it didn't exist
    """
    if service_utils.is_glance_image(href):
        # Glance image contents cannot be updated without changing image's UUID
        return os.path.exists(master_path)
    if os.path.exists(master_path):
        img_service = image_service.get_image_service(href, context=ctx)
        img_mtime = img_service.show(href).get('updated_at')
        if not img_mtime:
            # This means that href is not a glance image and doesn't have an
            # updated_at attribute
            LOG.warn(_LW("Image service couldn't determine last "
                         "modification time of %(href)s, considering "
                         "cached image up to date."), {'href': href})
            return True
        master_mtime = utils.unix_file_modification_datetime(master_path)
        if img_mtime <= master_mtime:
            return True
        # Delete image from cache as it is outdated
        LOG.info(_LI('Image %(href)s was last modified at %(remote_time)s. '
                     'Deleting the cached copy "%(cached_file)s since it was '
                     'last modified at %(local_time)s and may be outdated.'),
                 {'href': href, 'remote_time': img_mtime,
                  'local_time': master_mtime, 'cached_file': master_path})

        os.unlink(master_path)
    return False
Example #9
0
def rmtree_without_raise(path):
    try:
        if os.path.isdir(path):
            shutil.rmtree(path)
    except OSError as e:
        LOG.warn(_LW("Failed to remove dir %(path)s, error: %(e)s"),
                {'path': path, 'e': e})
Example #10
0
def _execute_ilo_clean_step(node, step, *args, **kwargs):
    """Executes a particular clean step.

    :param node: an Ironic node object.
    :param step: a clean step to be executed.
    :param args: The args to be passed to the clean step.
    :param kwargs: The kwargs to be passed to the clean step.
    :raises: NodeCleaningFailure, on failure to execute step.
    """
    ilo_object = ilo_common.get_ilo_object(node)

    try:
        clean_step = getattr(ilo_object, step)
    except AttributeError:
        # The specified clean step is not present in the proliantutils
        # package. Raise exception to update the proliantutils package
        # to newer version.
        raise exception.NodeCleaningFailure(
            _("Clean step '%s' not found. 'proliantutils' package needs to be "
              "updated.") % step)
    try:
        clean_step(*args, **kwargs)
    except ilo_error.IloCommandNotSupportedError:
        # This clean step is not supported on Gen8 and below servers.
        # Log the failure and continue with cleaning.
        LOG.warning(_LW("'%(step)s' clean step is not supported on node "
                        "%(uuid)s. Skipping the clean step."),
                    {'step': step, 'uuid': node.uuid})
    except ilo_error.IloError as ilo_exception:
        raise exception.NodeCleaningFailure(_(
            "Clean step %(step)s failed "
            "on node %(node)s with error: %(err)s") %
            {'node': node.uuid, 'step': step, 'err': ilo_exception})
Example #11
0
    def clean_up(self, amount=None):
        """Clean up directory with images, keeping cache of the latest images.

        Files with link count >1 are never deleted.
        Protected by global lock, so that no one messes with master images
        after we get listing and before we actually delete files.

        :param amount: if present, amount of space to reclaim in bytes,
                       cleaning will stop, if this goal was reached,
                       even if it is possible to clean up more files
        """
        if self.master_dir is None:
            return

        LOG.debug("Starting clean up for master image cache %(dir)s" %
                  {'dir': self.master_dir})

        amount_copy = amount
        listing = _find_candidates_for_deletion(self.master_dir)
        survived, amount = self._clean_up_too_old(listing, amount)
        if amount is not None and amount <= 0:
            return
        amount = self._clean_up_ensure_cache_size(survived, amount)
        if amount is not None and amount > 0:
            LOG.warn(_LW("Cache clean up was unable to reclaim %(required)d "
                       "MiB of disk space, still %(left)d MiB required"),
                     {'required': amount_copy / 1024 / 1024,
                      'left': amount / 1024 / 1024})
Example #12
0
    def get_boot_device(self, task):
        """Get the current boot device for the task's node.

        Provides the current boot device of the node. Be aware that not
        all drivers support this.

        :param task: a task from TaskManager.
        :raises: InvalidParameterValue 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.
        :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.

        """
        node = task.node
        driver_info = _parse_driver_info(node)
        driver_info['macs'] = driver_utils.get_node_mac_addresses(task)
        ssh_obj = _get_connection(node)
        response = {'boot_device': None, 'persistent': None}
        try:
            response['boot_device'] = _get_boot_device(ssh_obj, driver_info)
        except NotImplementedError:
            LOG.warning(_LW("Failed to get boot device for node %(node)s, "
                            "virt_type %(vtype)s does not support this "
                            "operation"),
                        {'node': node.uuid, 'vtype': driver_info['virt_type']})
        return response
Example #13
0
def remove_image_from_swift(object_name, associated_with=None):
    """Removes the given image from swift.

    This method removes the given image name from swift. It deletes the
    image if it exists in CONF.ilo.swift_ilo_container

    :param object_name: The name of the object which needs to be removed
                        from swift.
    :param associated_with: string to depict the component/operation this
                            object is associated to.
    """
    container = CONF.ilo.swift_ilo_container
    try:
        swift_api = swift.SwiftAPI()
        swift_api.delete_object(container, object_name)
    except exception.SwiftObjectNotFoundError as e:
        LOG.warning(
            _LW("Temporary object %(associated_with_msg)s"
                "was already deleted from Swift. Error: %(err)s"),
            {'associated_with_msg': ("associated with %s " % associated_with
                                     if associated_with else ""), 'err': e})
    except exception.SwiftOperationError as e:
        LOG.exception(
            _LE("Error while deleting temporary swift object %(object_name)s "
                "%(associated_with_msg)s from %(container)s. Error: %(err)s"),
            {'object_name': object_name, 'container': container,
             'associated_with_msg': ("associated with %s" % associated_with
                                     if associated_with else ""), 'err': e})
Example #14
0
    def get_boot_device(self, task):
        """Get the current boot device for a node.

        :param task: a task from TaskManager.
        :returns: a dictionary containing:
            'boot_device': one of the ironic.common.boot_devices or None
            'persistent': True if boot device is persistent, False otherwise
        :raises: MissingParameterValue, if some required parameter(s) are
            missing in the node's driver_info.
        :raises: InvalidParameterValue, if some parameter(s) have invalid
            value(s) in the node's driver_info.
        :raises: VirtualBoxOperationFailed, if error encountered from
            VirtualBox operation.
        """
        if task.driver.power.get_power_state(task) == states.POWER_OFF:
            ironic_boot_dev, persistent = \
                self._get_boot_device_from_hardware(task)
        else:
            ironic_boot_dev = task.node. \
                driver_internal_info.get('vbox_target_boot_device')
            if ironic_boot_dev is not None:
                msg = _LW("As ironic node %(node)s is"
                          " powered on, we will set to boot"
                          " from %(device)s before next boot.")
                LOG.warning(msg, {'node': task.node.uuid,
                                  'device': ironic_boot_dev})
                persistent = True
            else:
                # Maybe the vbox_target_boot_device is cleaned
                ironic_boot_dev, persistent = \
                    self._get_boot_device_from_hardware(task)
        return {'boot_device': ironic_boot_dev, 'persistent': persistent}
Example #15
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
Example #16
0
    def _clean_up_too_old(self, listing, amount):
        """Clean up stage 1: drop images that are older than TTL.

        This method removes files all files older than TTL seconds
        unless 'amount' is non-None. If 'amount' is non-None,
        it starts removing files older than TTL seconds,
        oldest first, until the required 'amount' of space is reclaimed.

        :param listing: list of tuples (file name, last used time)
        :param amount: if not None, amount of space to reclaim in bytes,
                       cleaning will stop, if this goal was reached,
                       even if it is possible to clean up more files
        :returns: tuple (list of files left after clean up,
                         amount still to reclaim)
        """
        threshold = time.time() - self._cache_ttl
        survived = []
        for file_name, last_used, stat in listing:
            if last_used < threshold:
                try:
                    os.unlink(file_name)
                except EnvironmentError as exc:
                    LOG.warn(_LW("Unable to delete file %(name)s from "
                                 "master image cache: %(exc)s"),
                             {'name': file_name, 'exc': exc})
                else:
                    if amount is not None:
                        amount -= stat.st_size
                        if amount <= 0:
                            amount = 0
                            break
            else:
                survived.append((file_name, last_used, stat))
        return survived, amount
Example #17
0
def try_set_boot_device(task, device, persistent=True):
    """Tries to set the boot device on the node.

    This method tries to set the boot device on the node to the given
    boot device.  Under uefi boot mode, setting of boot device may differ
    between different machines. IPMI does not work for setting boot
    devices in uefi mode for certain machines.  This method ignores the
    expected IPMI failure for uefi boot mode and just logs a message.
    In error cases, it is expected the operator has to manually set the
    node to boot from the correct device.

    :param task: a TaskManager object containing the node
    :param device: the boot device
    :param persistent: Whether to set the boot device persistently
    :raises: Any exception from set_boot_device except IPMIFailure
        (setting of boot device using ipmi is expected to fail).
    """
    try:
        manager_utils.node_set_boot_device(task, device,
                                           persistent=persistent)
    except exception.IPMIFailure:
        if get_boot_mode_for_deploy(task.node) == 'uefi':
            LOG.warning(_LW("ipmitool is unable to set boot device while "
                            "the node %s is in UEFI boot mode. Please set "
                            "the boot device manually.") % task.node.uuid)
        else:
            raise
Example #18
0
    def _get_port_ip_address(self, task, p_obj, client):
        """Get ip address of ironic port/portgroup assigned by Neutron.

        :param task: a TaskManager instance.
        :param p_obj: Ironic port or portgroup object.
        :param client: Neutron client instance.
        :returns: List of Neutron vif ip address associated with
            Node's port/portgroup.
        :raises: FailedToGetIPAddressOnPort
        :raises: InvalidIPv4Address
        """

        # NOTE(vdrok): This works because cleaning_vif_port_id doesn't exist
        # when we're in deployment/tenant network
        vif = (p_obj.internal_info.get('cleaning_vif_port_id') or
               p_obj.extra.get('vif_port_id'))
        if not vif:
            obj_name = 'portgroup'
            if isinstance(p_obj, objects.Port):
                obj_name = 'port'
            LOG.warning(_LW("No VIFs found for node %(node)s when attempting "
                            "to get IP address for %(obj_name)s: %(obj_id)."),
                        {'node': task.node.uuid, 'obj_name': obj_name,
                        'obj_id': p_obj.uuid})
            raise exception.FailedToGetIPAddressOnPort(port_id=p_obj.uuid)

        vif_ip_address = self._get_fixed_ip_address(vif, client)
        return vif_ip_address
Example #19
0
def _reboot(driver_info):
    """Reboot this node.

    If the power is off, turn it on. If the power is on, reset it.

    :param driver_info: the bmc access info for a node.
    :returns: power state POWER_ON, one of :class:`ironic.common.states`.
    :raises: IPMIFailure when the native ipmi call fails.
    :raises: PowerStateFailure when invalid power state is returned
             from ipmi.
    """

    msg = _LW("IPMI power reboot failed for node %(node_id)s with the "
              "following error: %(error)s")
    try:
        ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
                           userid=driver_info['username'],
                           password=driver_info['password'])
        wait = CONF.ipmi.retry_timeout
        ret = ipmicmd.set_power('boot', wait)
    except pyghmi_exception.IpmiException as e:
        LOG.warning(msg % {'node_id': driver_info['uuid'], 'error': str(e)})
        raise exception.IPMIFailure(cmd=str(e))

    state = ret.get('powerstate')
    if state == 'on':
        return states.POWER_ON
    else:
        LOG.warning(msg % {'node_id': driver_info['uuid'], 'error': ret})
        raise exception.PowerStateFailure(pstate=state)
Example #20
0
    def pass_bootloader_install_info(self, task, **kwargs):
        """Accepts the results of bootloader installation.

        This method acts as a vendor passthru and accepts the result of
        the bootloader installation. If bootloader installation was
        successful, then it notifies the bare metal to proceed to reboot
        and makes the instance active. If the bootloader installation failed,
        then it sets provisioning as failed and powers off the node.

        :param task: A TaskManager object.
        :param kwargs: The arguments sent with vendor passthru.  The expected
            kwargs are::

                'key': The deploy key for authorization
                'status': 'SUCCEEDED' or 'FAILED'
                'error': The error message if status == 'FAILED'
                'address': The IP address of the ramdisk

        """
        LOG.warning(
            _LW(
                "The node %s is using the bash deploy ramdisk for "
                "its deployment. This deploy ramdisk has been "
                "deprecated. Please use the ironic-python-agent "
                "(IPA) ramdisk instead."
            ),
            task.node.uuid,
        )
        task.process_event("resume")
        LOG.debug("Continuing the deployment on node %s", task.node.uuid)
        validate_bootloader_install_status(task, kwargs)
        finish_deploy(task, kwargs["address"])
Example #21
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)
Example #22
0
    def _initiate_cleaning(self, task):
        """Initiates the steps required to start cleaning for the node.

        This method polls each interface of the driver for getting the
        clean steps and notifies Ironic conductor to resume cleaning.
        On error, it sets the node to CLEANFAIL state and populates
        node.last_error with the error message.

        :param task: a TaskManager instance containing the node to act on.
        """
        LOG.warning(
            _LW(
                "Bash deploy ramdisk doesn't support in-band cleaning. "
                "Please use the ironic-python-agent (IPA) ramdisk "
                "instead for node %s. "
            ),
            task.node.uuid,
        )
        try:
            manager_utils.set_node_cleaning_steps(task)
            self.notify_conductor_resume_clean(task)
        except Exception as e:
            last_error = _(
                "Encountered exception for node %(node)s " "while initiating cleaning. Error:  %(error)s"
            ) % {"node": task.node.uuid, "error": e}
            return manager_utils.cleaning_error_handler(task, last_error)
Example #23
0
def check_image_size(task, image_source):
    """Check if the requested image is larger than the ram size.

    :param task: a TaskManager instance containing the node to act on.
    :param image_source: href of the image.
    :raises: InvalidParameterValue if size of the image is greater than
        the available ram size.
    """
    node = task.node
    properties = node.properties
    # skip check if 'memory_mb' is not defined
    if 'memory_mb' not in properties:
        LOG.warning(_LW('Skip the image size check as memory_mb is not '
                        'defined in properties on node %s.'), node.uuid)
        return

    image_show = images.image_show(task.context, image_source)
    if CONF.agent.stream_raw_images and image_show.get('disk_format') == 'raw':
        LOG.debug('Skip the image size check since the image is going to be '
                  'streamed directly onto the disk for node %s', node.uuid)
        return

    memory_size = int(properties.get('memory_mb'))
    image_size = int(image_show['size'])
    reserved_size = CONF.agent.memory_consumed_by_agent
    if (image_size + (reserved_size * units.Mi)) > (memory_size * units.Mi):
        msg = (_('Memory size is too small for requested image, if it is '
                 'less than (image size + reserved RAM size), will break '
                 'the IPA deployments. Image size: %(image_size)d MiB, '
                 'Memory size: %(memory_size)d MiB, Reserved size: '
                 '%(reserved_size)d MiB.')
               % {'image_size': image_size / units.Mi,
                  'memory_size': memory_size,
                  'reserved_size': reserved_size})
        raise exception.InvalidParameterValue(msg)
Example #24
0
    def get_ip_addresses(self, task):
        """Get IP addresses for all ports in `task`.

        :param task: a TaskManager instance.
        :returns: List of IP addresses associated with task.ports.
        """
        client = _build_client(task.context.auth_token)
        failures = []
        ip_addresses = []
        for port in task.ports:
            try:
                port_ip_address = self._get_port_ip_address(task, port.uuid,
                                                            client)
                ip_addresses.append(port_ip_address)
            except (exception.FailedToGetIPAddressOnPort,
                    exception.InvalidIPv4Address):
                failures.append(port.uuid)

        if failures:
            LOG.warn(_LW("Some errors were encountered on node %(node)s"
                         " while retrieving IP address on the following"
                         " ports: %(ports)s."),
                         {'node': task.node.uuid, 'ports': failures})

        return ip_addresses
def list_partitions(device):
    """Get partitions information from given device.

    :param device: The device path.
    :returns: list of dictionaries (one per partition) with keys:
              start, end, size (in MiB), filesystem, flags
    """
    output = utils.execute(
        'parted', '-s', '-m', device, 'unit', 'MiB', 'print',
        use_standard_locale=True)[0]
    lines = [line for line in output.split('\n') if line.strip()][2:]
    # Example of line: 1:1.00MiB:501MiB:500MiB:ext4::boot
    fields = ('start', 'end', 'size', 'filesystem', 'flags')
    result = []
    for line in lines:
        match = _PARTED_PRINT_RE.match(line)
        if match is None:
            LOG.warn(_LW("Partition information from parted for device "
                         "%(device)s does not match "
                         "expected format: %(line)s"),
                     dict(device=device, line=line))
            continue
        # Cast int fields to ints (some are floats and we round them down)
        groups = [int(float(x)) if i < 3 else x
                  for i, x in enumerate(match.groups())]
        result.append(dict(zip(fields, groups)))
    return result
Example #26
0
    def _get_ip_addresses(self, task, pobj_list, client):
        """Get IP addresses for all ports/portgroups.

        :param task: a TaskManager instance.
        :param pobj_list: List of port or portgroup objects.
        :param client: Neutron client instance.
        :returns: List of IP addresses associated with
                  task's ports/portgroups.
        """
        failures = []
        ip_addresses = []
        for obj in pobj_list:
            try:
                vif_ip_address = self._get_port_ip_address(task, obj,
                                                           client)
                ip_addresses.append(vif_ip_address)
            except (exception.FailedToGetIPAddressOnPort,
                    exception.InvalidIPv4Address):
                    failures.append(obj.uuid)

        if failures:
            obj_name = 'portgroups'
            if isinstance(pobj_list[0], objects.Port):
                obj_name = 'ports'

            LOG.warning(_LW(
                "Some errors were encountered on node %(node)s "
                "while retrieving IP addresses on the following "
                "%(obj_name)s: %(failures)s."),
                {'node': task.node.uuid, 'obj_name': obj_name,
                 'failures': failures})

        return ip_addresses
Example #27
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: InvalidParameterValue if required IPMI parameters
            are missing.
        :raises: IPMIFailure on an error from ipmitool.
        :raises: MissingParameterValue if a required parameter is missing.
        :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 = task.node.driver_info
        driver_internal_info = task.node.driver_internal_info

        if (driver_info.get('ipmi_force_boot_device', False) and
                driver_internal_info.get('persistent_boot_device') and
                driver_internal_info.get('is_next_boot_persistent', True)):
            return {
                'boot_device': driver_internal_info['persistent_boot_device'],
                'persistent': True
            }

        cmd = "chassis bootparam get 5"
        driver_info = _parse_driver_info(task.node)
        response = {'boot_device': None, 'persistent': None}

        try:
            out, err = _exec_ipmitool(driver_info, cmd)
        except (exception.PasswordFileFailedToCreate,
                processutils.ProcessExecutionError) as e:
            LOG.warning(_LW('IPMI get boot device failed for node %(node)s '
                            'when executing "ipmitool %(cmd)s". '
                            'Error: %(error)s'),
                        {'node': driver_info['uuid'], 'cmd': cmd, 'error': e})
            raise exception.IPMIFailure(cmd=cmd)

        re_obj = re.search('Boot Device Selector : (.+)?\n', out)
        if re_obj:
            boot_selector = re_obj.groups('')[0]
            if 'PXE' in boot_selector:
                response['boot_device'] = boot_devices.PXE
            elif 'Hard-Drive' in boot_selector:
                if 'Safe-Mode' in boot_selector:
                    response['boot_device'] = boot_devices.SAFE
                else:
                    response['boot_device'] = boot_devices.DISK
            elif 'BIOS' in boot_selector:
                response['boot_device'] = boot_devices.BIOS
            elif 'CD/DVD' in boot_selector:
                response['boot_device'] = boot_devices.CDROM

        response['persistent'] = 'Options apply to all future boots' in out
        return response
Example #28
0
def spawn_cleaning_error_handler(e, node):
    """Handle spawning error for node cleaning."""
    if isinstance(e, exception.NoFreeConductorWorker):
        node.last_error = (_("No free conductor workers available"))
        node.save()
        LOG.warning(_LW("No free conductor workers available to perform "
                        "cleaning on node %(node)s"), {'node': node.uuid})
Example #29
0
def provisioning_error_handler(e, node, provision_state,
                               target_provision_state):
    """Set the node's provisioning states if error occurs.

    This hook gets called upon an exception being raised when spawning
    the worker to do the deployment or tear down of a node.

    :param e: the exception object that was raised.
    :param node: an Ironic node object.
    :param provision_state: the provision state to be set on
        the node.
    :param target_provision_state: the target provision state to be
        set on the node.

    """
    if isinstance(e, exception.NoFreeConductorWorker):
        # NOTE(deva): there is no need to clear conductor_affinity
        #             because it isn't updated on a failed deploy
        node.provision_state = provision_state
        node.target_provision_state = target_provision_state
        node.last_error = (_("No free conductor workers available"))
        node.save()
        LOG.warning(_LW("No free conductor workers available to perform "
                        "an action on node %(node)s, setting node's "
                        "provision_state back to %(prov_state)s and "
                        "target_provision_state to %(tgt_prov_state)s."),
                    {'node': node.uuid, 'prov_state': provision_state,
                     'tgt_prov_state': target_provision_state})
Example #30
0
    def prepare_instance(self, task):
        """Prepares the boot of instance.

        This method prepares the boot of the instance after reading
        relevant information from the node's instance_info.
        It does the following depending on boot_option for deploy:

        - If the boot_option requested for this deploy is 'local' or image
          is a whole disk image, then it sets the node to boot from disk.
        - Otherwise it finds/creates the boot ISO to boot the instance
          image, attaches the boot ISO to the bare metal and then sets
          the node to boot from CDROM.

        :param task: a task from TaskManager.
        :returns: None
        :raises: IloOperationError, if some operation on iLO failed.
        """

        ilo_common.cleanup_vmedia_boot(task)

        # For iscsi_ilo driver, we boot from disk every time if the image
        # deployed is a whole disk image.
        node = task.node
        iwdi = node.driver_internal_info.get('is_whole_disk_image')
        if deploy_utils.get_boot_option(node) == "local" or iwdi:
            manager_utils.node_set_boot_device(task, boot_devices.DISK,
                                               persistent=True)
        else:
            drv_int_info = node.driver_internal_info
            root_uuid_or_disk_id = drv_int_info.get('root_uuid_or_disk_id')
            if root_uuid_or_disk_id:
                self._configure_vmedia_boot(task, root_uuid_or_disk_id)
            else:
                LOG.warning(_LW("The UUID for the root partition could not "
                                "be found for node %s"), node.uuid)
Example #31
0
    def clear_node_reservations_for_conductor(self, hostname):
        nodes = []
        with _session_for_write():
            query = (model_query(models.Node).filter_by(reservation=hostname))
            nodes = [node['uuid'] for node in query]
            query.update({'reservation': None})

        if nodes:
            nodes = ', '.join(nodes)
            LOG.warning(
                _LW('Cleared reservations held by %(hostname)s: '
                    '%(nodes)s'), {
                        'hostname': hostname,
                        'nodes': nodes
                    })
Example #32
0
def safe_rstrip(value, chars=None):
    """Removes trailing characters from a string if that does not make it empty

    :param value: A string value that will be stripped.
    :param chars: Characters to remove.
    :return: Stripped value.

    """
    if not isinstance(value, six.string_types):
        LOG.warn(
            _LW("Failed to remove trailing character. Returning original "
                "object. Supplied object is not a string: %s,"), value)
        return value

    return value.rstrip(chars) or value
Example #33
0
    def _wait_for_power_on(state, retries):
        """Called at an interval until the node is powered on."""

        state[0] = _get_power_status(node)
        if state[0] == states.POWER_ON:
            raise loopingcall.LoopingCallDone()

        if retries[0] > CONF.seamicro.max_retry:
            state[0] = states.ERROR
            raise loopingcall.LoopingCallDone()
        try:
            retries[0] += 1
            server.power_on()
        except seamicro_client_exception.ClientException:
            LOG.warning(_LW("Power-on failed for node %s."), node.uuid)
Example #34
0
def is_node_in_use_by_oneview(node):
    """Check if node is in use by OneView user.

    :param node: an ironic node object
    :returns: Boolean value. True if node is in use by OneView,
              False otherwise.
    :raises OneViewError: if not possible to get OneView's informations
             for the given node, if not possible to retrieve Server Hardware
             from OneView.

    """
    oneview_info = common.get_oneview_info(node)

    oneview_client = common.get_oneview_client()

    sh_uuid = oneview_utils.get_uuid_from_uri(
        oneview_info.get("server_hardware_uri"))

    try:
        server_hardware = oneview_client.get_server_hardware_by_uuid(sh_uuid)
    except oneview_exception.OneViewResourceNotFoundError as e:
        msg = (_("Error while obtaining Server Hardware from node "
                 "%(node_uuid)s. Error: %(error)s") % {
                     'node_uuid': node.uuid,
                     'error': e
                 })
        raise exception.OneViewError(error=msg)

    applied_sp_uri = (node.driver_info.get('applied_server_profile_uri'))

    # Check if Profile exists in Oneview and it is different of the one
    # applied by ironic
    if (server_hardware.server_profile_uri not in (None, '')
            and applied_sp_uri != server_hardware.server_profile_uri):

        LOG.warning(_LW("Node %s is already in use by OneView."), node.uuid)

        return True

    else:
        LOG.debug(
            _("Hardware %(hardware_uri)s is free for use by "
              "ironic on node %(node_uuid)s."), {
                  "hardware_uri": server_hardware.uri,
                  "node_uuid": node.uuid
              })

        return False
Example #35
0
    def reboot_and_finish_deploy(self, task):
        """Helper method to trigger reboot on the node and finish deploy.

        This method initiates a reboot on the node. On success, it
        marks the deploy as complete. On failure, it logs the error
        and marks deploy as failure.

        :param task: a TaskManager object containing the node
        :raises: InstanceDeployFailure, if node reboot failed.
        """
        wait = CONF.agent.post_deploy_get_power_state_retry_interval * 1000
        attempts = CONF.agent.post_deploy_get_power_state_retries + 1

        @retrying.retry(
            stop_max_attempt_number=attempts,
            retry_on_result=lambda state: state != states.POWER_OFF,
            wait_fixed=wait)
        def _wait_until_powered_off(task):
            return task.driver.power.get_power_state(task)

        node = task.node

        try:
            try:
                self._client.power_off(node)
                _wait_until_powered_off(task)
            except Exception as e:
                LOG.warning(
                    _LW('Failed to soft power off node %(node_uuid)s '
                        'in at least %(timeout)d seconds. Error: %(error)s'), {
                            'node_uuid': node.uuid,
                            'timeout': (wait * (attempts - 1)) / 1000,
                            'error': e
                        })
                manager_utils.node_power_action(task, states.POWER_OFF)

            manager_utils.node_set_boot_device(task, 'disk', persistent=True)
            manager_utils.node_power_action(task, states.POWER_ON)
        except Exception as e:
            msg = (_('Error rebooting node %(node)s after deploy. '
                     'Error: %(error)s') % {
                         'node': node.uuid,
                         'error': e
                     })
            agent_base_vendor.log_and_raise_deployment_error(task, msg)

        task.process_event('done')
        LOG.info(_LI('Deployment to node %s done'), task.node.uuid)
Example #36
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: InvalidParameterValue if required IPMI parameters
            are missing.
        :raises: IPMIFailure on an error from ipmitool.
        :raises: MissingParameterValue if a required parameter is missing.
        :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.

        """
        cmd = "chassis bootparam get 5"
        driver_info = _parse_driver_info(task.node)
        response = {'boot_device': None, 'persistent': None}
        try:
            out, err = _exec_ipmitool(driver_info, cmd)
        except (exception.PasswordFileFailedToCreate,
                processutils.ProcessExecutionError) as e:
            LOG.warning(_LW('IPMI get boot device failed for node %(node)s '
                            'when executing "ipmitool %(cmd)s". '
                            'Error: %(error)s'),
                        {'node': driver_info['uuid'], 'cmd': cmd, 'error': e})
            raise exception.IPMIFailure(cmd=cmd)

        re_obj = re.search('Boot Device Selector : (.+)?\n', out)
        if re_obj:
            boot_selector = re_obj.groups('')[0]
            if 'PXE' in boot_selector:
                response['boot_device'] = boot_devices.PXE
            elif 'Hard-Drive' in boot_selector:
                if 'Safe-Mode' in boot_selector:
                    response['boot_device'] = boot_devices.SAFE
                else:
                    response['boot_device'] = boot_devices.DISK
            elif 'BIOS' in boot_selector:
                response['boot_device'] = boot_devices.BIOS
            elif 'CD/DVD' in boot_selector:
                response['boot_device'] = boot_devices.CDROM

        response['persistent'] = 'Options apply to all future boots' in out
        return response
Example #37
0
def _disable_secure_boot_if_supported(task):
    """Disables secure boot on node, does not throw if its not supported.

    :param task: a TaskManager instance containing the node to act on.
    :raises: IloOperationError, if some operation on iLO failed.
    """
    try:
        ilo_common.update_secure_boot_mode(task, False)
    # We need to handle IloOperationNotSupported exception so that if
    # the user has incorrectly specified the Node capability
    # 'secure_boot' to a node that does not have that capability and
    # attempted deploy. Handling this exception here, will help the
    # user to tear down such a Node.
    except exception.IloOperationNotSupported:
        LOG.warning(_LW('Secure boot mode is not supported for node %s'),
                    task.node.uuid)
Example #38
0
def stop_shellinabox_console(node_uuid):
    """Close the serial console for a node.

    :param node_uuid: the UUID of the node
    :raises: ConsoleError if unable to stop the console process
    """

    try:
        _stop_console(node_uuid)
    except exception.NoConsolePid:
        LOG.warning(_LW("No console pid found for node %s while trying to "
                        "stop shellinabox console."), node_uuid)
    except processutils.ProcessExecutionError as exc:
            msg = (_("Could not stop the console for node '%(node)s'. "
                     "Reason: %(err)s.") % {'node': node_uuid, 'err': exc})
            raise exception.ConsoleError(message=msg)
Example #39
0
    def create_cleaning_ports(self, task):
        """Create neutron ports for each port on task.node to boot the ramdisk.

        :param task: a TaskManager instance.
        :raises: NetworkError, InvalidParameterValue
        :returns: a dictionary in the form {port.uuid: neutron_port['id']}
        """
        global create_cleaning_ports_deprecation
        if not create_cleaning_ports_deprecation:
            LOG.warning(
                _LW('create_cleaning_ports via dhcp provider is '
                    'deprecated. The node.network_interface setting '
                    'should be used instead.'))
            create_cleaning_ports_deprecation = True

        return task.driver.network.add_cleaning_network(task)
Example #40
0
def try_set_boot_device(task, device, persistent=True):
    # NOTE(faizan): Under UEFI boot mode, setting of boot device may differ
    # between different machines. IPMI does not work for setting boot
    # devices in UEFI mode for certain machines.
    # Expected IPMI failure for uefi boot mode. Logging a message to
    # set the boot device manually and continue with deploy.
    try:
        manager_utils.node_set_boot_device(task, device, persistent=persistent)
    except exception.IPMIFailure:
        if driver_utils.get_node_capability(task.node,
                                            'boot_mode') == 'uefi':
            LOG.warning(_LW("ipmitool is unable to set boot device while "
                            "the node %s is in UEFI boot mode. Please set "
                            "the boot device manually.") % task.node.uuid)
        else:
            raise
Example #41
0
    def _find_ports_by_macs(self, context, mac_addresses):
        """Given a list of MAC addresses, find the ports that match the MACs
        and return them as a list of Port objects, or an empty list if there
        are no matches
        """
        ports = []
        for mac in mac_addresses:
            # Will do a search by mac if the mac isn't malformed
            try:
                port_ob = objects.Port.get_by_address(context, mac)
                ports.append(port_ob)

            except exception.PortNotFound:
                LOG.warning(_LW('MAC address %s not found in database'), mac)

        return ports
Example #42
0
    def vif_list(self, task):
        """List attached VIF IDs for a node

        :param task: A TaskManager instance.
        :returns: List of VIF dictionaries, each dictionary will have an 'id'
            entry with the ID of the VIF.
        """
        default_impl = net_common.VIFPortIDMixin()
        if not self._deprecated_vif_list_shown:
            self.__class__._deprecated_vif_list_shown = True
            LOG.warning(
                _LW('The network interface %s should be updated to '
                    'implement the vif_list function. Falling '
                    'back to default implementation, this behaviour '
                    'will be removed in Pike'), self.__class__.__name__)
        return default_impl.vif_list(task)
Example #43
0
    def port_changed(self, task, port_obj):
        """Handle any actions required when a port changes

        :param task: a TaskManager instance.
        :param port_obj: a changed Port object.
        :raises: Conflict, FailedToUpdateDHCPOptOnPort
        """
        default_impl = net_common.VIFPortIDMixin()
        if not self._deprecated_port_change_shown:
            self.__class__._deprecated_port_change_shown = True
            LOG.warning(
                _LW('The network interface %s should be updated to '
                    'implement the port_changed function. Falling '
                    'back to default implementation, this behaviour '
                    'will be removed in Pike'), self.__class__.__name__)
        return default_impl.port_changed(task, port_obj)
Example #44
0
    def vif_detach(self, task, vif_id):
        """Detach a virtual network interface from a node

        :param task: A TaskManager instance.
        :param vif_id: A VIF ID to detach
        :raises: NetworkError, VifNotAttached
        """
        default_impl = net_common.VIFPortIDMixin()
        if not self._deprecated_vif_detach_shown:
            self.__class__._deprecated_vif_detach_shown = True
            LOG.warning(
                _LW('The network interface %s should be updated to '
                    'implement the vif_detach function. Falling '
                    'back to default implementation, this behaviour '
                    'will be removed in Pike'), self.__class__.__name__)
        return default_impl.vif_detach(task, vif_id)
Example #45
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
        """
        global update_port_address_deprecation
        if not update_port_address_deprecation:
            LOG.warning(
                _LW('update_port_address via DHCP provider is '
                    'deprecated. The node.network_interface '
                    'port_changed() should be used instead.'))
            update_port_address_deprecation = True

        neutron.update_port_address(port_id, address, token)
Example #46
0
    def _snmp_power_state(self):
        state = self.client.get(self.oid)

        # Translate the state to an Ironic power state.
        if state == self.value_power_on:
            power_state = states.POWER_ON
        elif state == self.value_power_off:
            power_state = states.POWER_OFF
        else:
            LOG.warning(_LW("SNMP PDU %(addr)s outlet %(outlet)s: "
                            "unrecognised power state %(state)s."),
                        {'addr': self.snmp_info['address'],
                         'outlet': self.snmp_info['outlet'],
                         'state': state})
            power_state = states.ERROR

        return power_state
Example #47
0
    def update_dhcp_opts(self, task, options):
        """Send or update the DHCP BOOT options for this node.

        :param task: A TaskManager instance.
        :param dhcp_opts: 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'}]
        """
        vifs = network.get_node_vif_ids(task)
        if not vifs:
            raise exception.FailedToUpdateDHCPOptOnPort(
                _("No VIFs found for node %(node)s when attempting "
                  "to update DHCP BOOT options.") %
                {'node': task.node.uuid})

        failures = []
        for port_id, port_vif in vifs.items():
            try:
                self.update_port_dhcp_opts(port_vif, options,
                                           token=task.context.auth_token)
            except exception.FailedToUpdateDHCPOptOnPort:
                failures.append(port_id)

        if failures:
            if len(failures) == len(vifs):
                raise exception.FailedToUpdateDHCPOptOnPort(_(
                    "Failed to set DHCP BOOT options for any port on node %s.")
                    % task.node.uuid)
            else:
                LOG.warning(_LW("Some errors were encountered when updating "
                                "the DHCP BOOT options for node %(node)s on "
                                "the following ports: %(ports)s."),
                            {'node': task.node.uuid, 'ports': failures})

        # TODO(adam_g): Hack to workaround bug 1334447 until we have a
        # mechanism for synchronizing events with Neutron.  We need to sleep
        # only if we are booting VMs, which is implied by SSHPower, to ensure
        # they do not boot before Neutron agents have setup sufficent DHCP
        # config for netboot.
        if isinstance(task.driver.power, ssh.SSHPower):
            LOG.debug("Waiting 15 seconds for Neutron.")
            time.sleep(15)
Example #48
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
        :raises: MissingParameterValue if required ipmi parameters are missing.
        :raises: IPMIFailure on an error from ipmitool.

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

        # note(JayF): IPMI spec indicates unless you send these raw bytes the
        # boot device setting times out after 60s. Since it's possible it
        # could be >60s before a node is rebooted, we should always send them.
        # This mimics pyghmi's current behavior, and the "option=timeout"
        # setting on newer ipmitool binaries.
        timeout_disable = "0x00 0x08 0x03 0x08"
        _send_raw(task, timeout_disable)

        cmd = "chassis bootdev %s" % device
        if persistent:
            cmd = cmd + " options=persistent"
        driver_info = _parse_driver_info(task.node)
        try:
            out, err = _exec_ipmitool(driver_info, cmd)
        except (exception.PasswordFileFailedToCreate,
                processutils.ProcessExecutionError) as e:
            LOG.warning(
                _LW('IPMI set boot device failed for node %(node)s '
                    'when executing "ipmitool %(cmd)s". '
                    'Error: %(error)s'), {
                        'node': driver_info['uuid'],
                        'cmd': cmd,
                        'error': e
                    })
            raise exception.IPMIFailure(cmd=cmd)
Example #49
0
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None and self._spawn_method is not None:
            # Spawn a worker to complete the task
            # The linked callback below will be called whenever:
            #   - background task finished with no errors.
            #   - background task has crashed with exception.
            #   - callback was added after the background task has
            #     finished or crashed. While eventlet currently doesn't
            #     schedule the new thread until the current thread blocks
            #     for some reason, this is true.
            # All of the above are asserted in tests such that we'll
            # catch if eventlet ever changes this behavior.
            fut = None
            try:
                fut = self._spawn_method(*self._spawn_args,
                                         **self._spawn_kwargs)

                # NOTE(comstud): Trying to use a lambda here causes
                # the callback to not occur for some reason. This
                # also makes it easier to test.
                fut.add_done_callback(self._thread_release_resources)
                # Don't unlock! The unlock will occur when the
                # thread finishes.
                return
            except Exception as e:
                with excutils.save_and_reraise_exception():
                    try:
                        # Execute the on_error hook if set
                        if self._on_error_method:
                            self._on_error_method(e, *self._on_error_args,
                                                  **self._on_error_kwargs)
                    except Exception:
                        LOG.warning(
                            _LW("Task's on_error hook failed to "
                                "call %(method)s on node %(node)s"), {
                                    'method': self._on_error_method.__name__,
                                    'node': self.node.uuid
                                })

                    if fut is not None:
                        # This means the add_done_callback() failed for some
                        # reason. Nuke the thread.
                        fut.cancel()
                    self.release_resources()
        self.release_resources()
Example #50
0
def _check_auth_options(conf):
    missing = []
    for section in SECTIONS_WITH_AUTH:
        if not auth.load_auth(conf, section):
            missing.append('[%s]' % section)
    if missing:
        link = "http://docs.openstack.org/releasenotes/ironic/newton.html"
        LOG.warning(
            _LW("Failed to load authentification credentials from "
                "%(missing)s config sections. "
                "The corresponding service users' credentials "
                "will be loaded from [%(old)s] config section, "
                "which is deprecated for this purpose. "
                "Please update the config file. "
                "For more info see %(link)s."),
            dict(missing=", ".join(missing),
                 old=auth.LEGACY_SECTION,
                 link=link))
Example #51
0
    def clear_node_target_power_state(self, hostname):
        nodes = []
        with _session_for_write():
            query = (model_query(models.Node)
                     .filter_by(reservation=hostname))
            query = query.filter(models.Node.target_power_state != sql.null())
            nodes = [node['uuid'] for node in query]
            query.update({'target_power_state': None,
                          'last_error': _("Pending power operation was "
                                          "aborted due to conductor "
                                          "restart")})

        if nodes:
            nodes = ', '.join(nodes)
            LOG.warning(
                _LW('Cleared target_power_state of the locked nodes in '
                    'powering process, their power state can be incorrect: '
                    '%(nodes)s'), {'nodes': nodes})
Example #52
0
    def _snmp_power_state(self):
        oid = self._snmp_oid(self.oid_status)
        state = self.client.get(oid)

        # Translate the state to an Ironic power state.
        if state in (self.status_on, self.status_pending_off):
            power_state = states.POWER_ON
        elif state in (self.status_off, self.status_pending_on):
            power_state = states.POWER_OFF
        else:
            LOG.warning(_LW("Eaton Power SNMP PDU %(addr)s outlet %(outlet)s: "
                            "unrecognised power state %(state)s."),
                        {'addr': self.snmp_info['address'],
                         'outlet': self.snmp_info['outlet'],
                         'state': state})
            power_state = states.ERROR

        return power_state
Example #53
0
    def tear_down(self, task):
        """Tear down a previous deployment on the task's node.

        :param task: a TaskManager instance.
        :returns: states.DELETED
        """
        manager_utils.node_power_action(task, states.POWER_OFF)
        try:
            _update_secure_boot_mode(task, False)
        # We need to handle IloOperationNotSupported exception so that if
        # User had incorrectly specified the Node capability 'secure_boot'
        # to a node that do not have such capability and attempted deploy.
        # Handling this exception here, will help user to tear down such a
        # Node.
        except exception.IloOperationNotSupported:
            LOG.warn(_LW('Secure boot mode is not supported for node %s'),
                     task.node.uuid)
        return states.DELETED
Example #54
0
    def vif_attach(self, task, vif_info):
        """Attach a virtual network interface to a node

        :param task: A TaskManager instance.
        :param vif_info: a dictionary of information about a VIF.
            It must have an 'id' key, whose value is a unique identifier
            for that VIF.
        :raises: NetworkError, VifAlreadyAttached, NoFreePhysicalPorts
        """
        default_impl = net_common.VIFPortIDMixin()
        if not self._deprecated_vif_attach_shown:
            self.__class__._deprecated_vif_attach_shown = True
            LOG.warning(
                _LW('The network interface %s should be updated to '
                    'implement the vif_attach function. Falling '
                    'back to default implementation, this behaviour '
                    'will be removed in Pike'), self.__class__.__name__)
        return default_impl.vif_attach(task, vif_info)
Example #55
0
    def _clean_up_ensure_cache_size(self, listing, amount):
        """Clean up stage 2: try to ensure cache size < threshold.

        Try to delete the oldest files until conditions is satisfied
        or no more files are eligible for deletion.

        :param listing: list of tuples (file name, last used time)
        :param amount: amount of space to reclaim, if possible.
                       if amount is not None, it has higher priority than
                       cache size in settings
        :returns: amount of space still required after clean up
        """
        # NOTE(dtantsur): Sort listing to delete the oldest files first
        listing = sorted(listing, key=lambda entry: entry[1], reverse=True)
        total_listing = (os.path.join(self.master_dir, f)
                         for f in os.listdir(self.master_dir))
        total_size = sum(os.path.getsize(f) for f in total_listing)
        while listing and (total_size > self._cache_size or
                           (amount is not None and amount > 0)):
            file_name, last_used, stat = listing.pop()
            try:
                os.unlink(file_name)
            except EnvironmentError as exc:
                LOG.warn(
                    _LW("Unable to delete file %(name)s from "
                        "master image cache: %(exc)s"), {
                            'name': file_name,
                            'exc': exc
                        })
            else:
                total_size -= stat.st_size
                if amount is not None:
                    amount -= stat.st_size

        if total_size > self._cache_size:
            LOG.info(
                _LI("After cleaning up cache dir %(dir)s "
                    "cache size %(actual)d is still larger than "
                    "threshold %(expected)d"), {
                        'dir': self.master_dir,
                        'actual': total_size,
                        'expected': self._cache_size
                    })
        return max(amount, 0) if amount is not None else 0
Example #56
0
    def clean_up_instance(self, task):
        """Cleans up the boot of instance.

        This method cleans up the environment that was setup for booting
        the instance. It unlinks the instance kernel/ramdisk in node's
        directory in tftproot and removes the PXE config.

        :param task: a task from TaskManager.
        :returns: None
        """
        node = task.node
        try:
            images_info = _get_instance_image_info(node, task.context)
        except exception.MissingParameterValue as e:
            LOG.warning(_LW('Could not get instance image info '
                            'to clean up images for node %(node)s: %(err)s'),
                        {'node': node.uuid, 'err': e})
        else:
            _clean_up_pxe_env(task, images_info)
Example #57
0
def validate_port_info(node, port):
    """Check that port contains enough information for deploy.

    Neutron network interface requires that local_link_information field is
    filled before we can use this port.

    :param node: Ironic node object.
    :param port: Ironic port object.
    :returns: True if port info is valid, False otherwise.
    """
    if (node.network_interface == 'neutron' and
            not port.local_link_connection):
        LOG.warning(_LW("The local_link_connection is required for "
                        "'neutron' network interface and is not present "
                        "in the nodes %(node)s port %(port)s"),
                    {'node': node.uuid, 'port': port.uuid})
        return False

    return True
Example #58
0
def enforce(rule, target, creds, do_raise=False, exc=None, *args, **kwargs):
    """A shortcut for policy.Enforcer.enforce()

    Checks authorization of a rule against the target and credentials.
    Always returns true if CONF.auth_strategy == noauth.

    """
    # NOTE(deva): this method is obsoleted by authorize(), but retained for
    # backwards compatibility in case it has been used downstream.
    # It may be removed in the Pike cycle.
    LOG.warning(_LW(
        "Deprecation warning: calls to ironic.common.policy.enforce() "
        "should be replaced with authorize(). This method may be removed "
        "in a future release."))
    if CONF.auth_strategy == 'noauth':
        return True
    enforcer = get_enforcer()
    return enforcer.enforce(rule, target, creds, do_raise=do_raise,
                            exc=exc, *args, **kwargs)
Example #59
0
    def deploy(self, task):
        """Start deployment of the task's node'.

        Fetches instance image, creates a temporary keystone token file,
        updates the DHCP port options for next boot, and issues a reboot
        request to the power driver.
        This causes the node to boot into the deployment ramdisk and triggers
        the next phase of PXE-based deployment via
        VendorPassthru._continue_deploy().

        :param task: a TaskManager instance containing the node to act on.
        :returns: deploy state DEPLOYWAIT.
        """
        iscsi_deploy.cache_instance_image(task.context, task.node)
        iscsi_deploy.check_image_size(task)

        # TODO(yuriyz): more secure way needed for pass auth token
        #               to deploy ramdisk
        _create_token_file(task)
        dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
        provider = dhcp_factory.DHCPFactory()
        provider.update_dhcp(task, dhcp_opts)

        # NOTE(faizan): Under UEFI boot mode, setting of boot device may differ
        # between different machines. IPMI does not work for setting boot
        # devices in UEFI mode for certain machines.
        # Expected IPMI failure for uefi boot mode. Logging a message to
        # set the boot device manually and continue with deploy.
        try:
            manager_utils.node_set_boot_device(task, 'pxe', persistent=True)
        except exception.IPMIFailure:
            if driver_utils.get_node_capability(task.node,
                                                'boot_mode') == 'uefi':
                LOG.warning(
                    _LW("ipmitool is unable to set boot device while "
                        "the node is in UEFI boot mode."
                        "Please set the boot device manually."))
            else:
                raise

        manager_utils.node_power_action(task, states.REBOOT)

        return states.DEPLOYWAIT
Example #60
0
    def set_boot_device(self, task, device, persistent=False):
        """Set the boot device for a node.

        :param task: a task from TaskManager.
        :param device: ironic.common.boot_devices
        :param persistent: This argument is ignored as VirtualBox support only
            persistent boot devices.
        :raises: MissingParameterValue, if some required parameter(s) are
            missing in the node's driver_info.
        :raises: InvalidParameterValue, if some parameter(s) have invalid
            value(s) in the node's driver_info.
        :raises: VirtualBoxOperationFailed, if error encountered from
            VirtualBox operation.
        """
        # NOTE(rameshg87): VirtualBox has only persistent boot devices.
        try:
            boot_dev = IRONIC_TO_VIRTUALBOX_DEVICE_MAPPING[device]
        except KeyError:
            raise exception.InvalidParameterValue(
                _("Invalid boot device %s specified.") % device)

        if task.driver.power.get_power_state(task) == states.POWER_OFF:

            _run_virtualbox_method(task.node, 'set_boot_device',
                                   'set_boot_device', boot_dev)
        else:
            LOG.warning(
                _LW('Node %(node_uuid)s: As VirtualBox do not support '
                    'setting boot device when VM is powered on, we '
                    'will set booting from %(device)s when reboot '
                    'next time.'), {
                        'node_uuid': task.node.uuid,
                        'device': device
                    })
            # We should store target boot device in case the
            # end user shutoff the baremetal machine from NOVA API.
            boot_device_now = self.get_boot_device(task)['boot_device']
            if device != boot_device_now:
                driver_internal_info = task.node.driver_internal_info
                driver_internal_info['vbox_target_boot_device'] = device
                task.node.driver_internal_info = driver_internal_info
                task.node.save()