def _validate_statistics_methods(self, method, **kwargs):
        jsonschema.validate(kwargs, self.statistics_schema)

        global_params = ('unhandled_requests', 'response_time',
                         'cpu_throttling', 'memory_throttling',
                         'communication_failures')

        if kwargs['scope'] == 'policy' and 'policy_id' not in kwargs:
                raise exception.MissingParameterValue(_('Missing "policy_id"'))

        if kwargs.get('parameter_name') not in global_params:
            if 'domain_id' not in kwargs:
                raise exception.MissingParameterValue(_('Missing "domain_id"'))

        if method == 'reset_nm_statistics':
            if 'parameter_name' in kwargs:
                if kwargs['parameter_name'] not in global_params:
                    raise exception.InvalidParameterValue(
                        _('Invalid parameter name for resetting statistic, '
                          'individual reset is possible only for: %s') %
                        ', '.join(global_params))

        elif method == 'get_nm_statistics':
            if 'parameter_name' not in kwargs:
                raise exception.MissingParameterValue(
                    _('Parameter name is mandatory for getting statistics'))
            # valid parameters depend on scope
            if (kwargs['parameter_name'] not in
                nm_commands.STATISTICS[kwargs['scope']]):
                    raise exception.InvalidParameterValue(
                        _('Invalid parameter name %(param)% for scope '
                          '%(scope)s') % {'param': kwargs['parameter_name'],
                                          'scope': kwargs['scope']})
    def _validate_policy_methods(self, method, **kwargs):
        if method in ('get_nm_policy', 'remove_nm_policy',
                      'get_nm_policy_suspend', 'remove_nm_policy_suspend'):
            jsonschema.validate(kwargs, self.main_ids_schema)

        elif method == 'control_nm_policy':
            jsonschema.validate(kwargs, self.control_schema)
            if kwargs['scope'] != 'global' and 'domain_id' not in kwargs:
                raise exception.MissingParameterValue(_('Missing "domain_id"'))
            if kwargs['scope'] == 'policy' and 'policy_id' not in kwargs:
                raise exception.MissingParameterValue(_('Missing "policy_id"'))

        elif method == 'set_nm_policy':
            jsonschema.validate(kwargs, self.policy_schema)
            if kwargs['policy_trigger'] == 'boot':
                if not isinstance(kwargs['target_limit'], dict):
                    raise exception.InvalidParameterValue(_('Invalid boot '
                                                            'policy'))
            elif 'correction_time' not in kwargs:
                raise exception.MissingParameterValue(
                    _('Missing "correction_time" for no-boot policy'))

        elif method == 'set_nm_policy_suspend':
            jsonschema.validate(kwargs, self.suspend_schema)

        elif method == 'get_nm_capabilities':
            jsonschema.validate(kwargs, self.get_cap_schema)
def _parse_driver_info(node):
    info = node.driver_info or {}
    missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
    if missing_info:
        raise ironic_exception.MissingParameterValue(
            _("Missing the following iBoot credentials in node's"
              " driver_info: %s.") % missing_info)

    address = info['iboot_address']
    username = info['iboot_username']
    password = info['iboot_password']

    relay_id = info.get('iboot_relay_id', 1)
    try:
        relay_id = int(relay_id)
    except ValueError:
        raise ironic_exception.InvalidParameterValue(
            _("iBoot PDU relay id must be an integer."))

    port = info.get('iboot_port', 9100)
    port = utils.validate_network_port(port, 'iboot_port')

    return {
        'address': address,
        'username': username,
        'password': password,
        'port': port,
        'relay_id': relay_id,
        'uuid': node.uuid,
    }
    def _validate_policy_methods(self, method, **kwargs):
        if method in ('get_nm_policy', 'remove_nm_policy',
                      'get_nm_policy_suspend', 'remove_nm_policy_suspend'):
            jsonschema.validate(kwargs, self.main_ids_schema)

        elif method == 'control_nm_policy':
            jsonschema.validate(kwargs, self.control_schema)
            if kwargs['scope'] != 'global' and 'domain_id' not in kwargs:
                raise exception.MissingParameterValue(_('Missing "domain_id"'))
            if kwargs['scope'] == 'policy' and 'policy_id' not in kwargs:
                raise exception.MissingParameterValue(_('Missing "policy_id"'))

        elif method == 'set_nm_policy':
            jsonschema.validate(kwargs, self.policy_schema)
            if kwargs['policy_trigger'] == 'boot':
                if not isinstance(kwargs['target_limit'], dict):
                    raise exception.InvalidParameterValue(
                        _('Invalid boot '
                          'policy'))
            elif 'correction_time' not in kwargs:
                raise exception.MissingParameterValue(
                    _('Missing "correction_time" for no-boot policy'))

        elif method == 'set_nm_policy_suspend':
            jsonschema.validate(kwargs, self.suspend_schema)

        elif method == 'get_nm_capabilities':
            jsonschema.validate(kwargs, self.get_cap_schema)
def _parse_driver_info(node):
    info = node.driver_info or {}
    missing_info = [key for key in REQUIRED_PROPERTIES if not info.get(key)]
    if missing_info:
        raise ironic_exception.MissingParameterValue(
            _("Missing the following iBoot credentials in node's"
              " driver_info: %s.") % missing_info)

    address = info['iboot_address']
    username = info['iboot_username']
    password = info['iboot_password']

    relay_id = info.get('iboot_relay_id', 1)
    try:
        relay_id = int(relay_id)
    except ValueError:
        raise ironic_exception.InvalidParameterValue(
            _("iBoot PDU relay id must be an integer."))

    port = info.get('iboot_port', 9100)
    port = utils.validate_network_port(port, 'iboot_port')

    return {
        'address': address,
        'username': username,
        'password': password,
        'port': port,
        'relay_id': relay_id,
        'uuid': node.uuid,
    }
def _get_nm_address(task):
    """Get Intel Node Manager target channel and address.

    :param task: a TaskManager instance.
    :raises: IPMIFailure if Intel Node Manager is not detected on a node or if
             an error happens during detection.
    :returns: a tuple with IPMI channel and address of Intel Node Manager.
    """
    node = task.node
    driver_internal_info = node.driver_internal_info

    def _save_to_node(channel, address):
        driver_internal_info['intel_nm_channel'] = channel
        driver_internal_info['intel_nm_address'] = address
        node.driver_internal_info = driver_internal_info
        node.save()

    channel = driver_internal_info.get('intel_nm_channel')
    address = driver_internal_info.get('intel_nm_address')
    if channel and address:
        return channel, address
    if channel is False and address is False:
        raise exception.IPMIFailure(
            _('Driver data indicates that Intel '
              'Node Manager detection failed.'))
    LOG.info(_LI('Start detection of Intel Node Manager on node %s'),
             node.uuid)
    sdr_filename = os.path.join(CONF.tempdir, node.uuid + '.sdr')
    res = None
    try:
        ipmitool.dump_sdr(task, sdr_filename)
        res = nm_commands.parse_slave_and_channel(sdr_filename)
    finally:
        ironic_utils.unlink_without_raise(sdr_filename)
    if res is None:
        _save_to_node(False, False)
        raise exception.IPMIFailure(_('Intel Node Manager is not detected.'))
    address, channel = res
    LOG.debug(
        'Intel Node Manager sensors present in SDR on node %(node)s, '
        'channel %(channel)s address %(address)s.', {
            'node': node.uuid,
            'channel': channel,
            'address': address
        })
    # SDR can contain wrong info, try simple command
    node.driver_info['ipmi_bridging'] = 'single'
    node.driver_info['ipmi_target_channel'] = channel
    node.driver_info['ipmi_target_address'] = address
    try:
        ipmitool.send_raw(task,
                          _command_to_string(nm_commands.get_version(None)))
        _save_to_node(channel, address)
        return channel, address
    except exception.IPMIFailure:
        _save_to_node(False, False)
        raise exception.IPMIFailure(
            _('Intel Node Manager sensors record '
              'present in SDR but Node Manager is not '
              'responding.'))
    def _validate_statistics_methods(self, method, **kwargs):
        jsonschema.validate(kwargs, self.statistics_schema)

        global_params = ('unhandled_requests', 'response_time',
                         'cpu_throttling', 'memory_throttling',
                         'communication_failures')

        if kwargs['scope'] == 'policy' and 'policy_id' not in kwargs:
            raise exception.MissingParameterValue(_('Missing "policy_id"'))

        if kwargs.get('parameter_name') not in global_params:
            if 'domain_id' not in kwargs:
                raise exception.MissingParameterValue(_('Missing "domain_id"'))

        if method == 'reset_nm_statistics':
            if 'parameter_name' in kwargs:
                if kwargs['parameter_name'] not in global_params:
                    raise exception.InvalidParameterValue(
                        _('Invalid parameter name for resetting statistic, '
                          'individual reset is possible only for: %s') %
                        ', '.join(global_params))

        elif method == 'get_nm_statistics':
            if 'parameter_name' not in kwargs:
                raise exception.MissingParameterValue(
                    _('Parameter name is mandatory for getting statistics'))
            # valid parameters depend on scope
            if (kwargs['parameter_name']
                    not in nm_commands.STATISTICS[kwargs['scope']]):
                raise exception.InvalidParameterValue(
                    _('Invalid parameter name %(param)% for scope '
                      '%(scope)s') % {
                          'param': kwargs['parameter_name'],
                          'scope': kwargs['scope']
                      })
def _ipmi_timestamp_to_isotime(timestamp):
    """Convert IPMI timestamp to iso8601."""
    if timestamp == _UNSPECIFIED_TIMESTAMP:
        raise exception.InvalidIPMITimestamp(_('IPMI timestamp is invalid or '
                                               'unspecified'))
    if timestamp <= _INIT_TIMESTAMP_MAX:
        raise exception.InvalidIPMITimestamp(_('IPMI initialization is not '
                                               'completed, relative time is '
                                               '%d second') % timestamp)

    return datetime.datetime.utcfromtimestamp(timestamp).isoformat()
def _get_nm_address(task):
    """Get Intel Node Manager target channel and address.

    :param task: a TaskManager instance.
    :raises: IPMIFailure if Intel Node Manager is not detected on a node or if
             an error happens during detection.
    :returns: a tuple with IPMI channel and address of Intel Node Manager.
    """
    node = task.node
    driver_internal_info = node.driver_internal_info

    def _save_to_node(channel, address):
        driver_internal_info['intel_nm_channel'] = channel
        driver_internal_info['intel_nm_address'] = address
        node.driver_internal_info = driver_internal_info
        node.save()

    channel = driver_internal_info.get('intel_nm_channel')
    address = driver_internal_info.get('intel_nm_address')
    if channel and address:
        return channel, address
    if channel is False and address is False:
        raise exception.IPMIFailure(_('Driver data indicates that Intel '
                                      'Node Manager detection failed.'))
    LOG.info(_LI('Start detection of Intel Node Manager on node %s'),
             node.uuid)
    sdr_filename = os.path.join(CONF.tempdir, node.uuid + '.sdr')
    res = None
    try:
        ipmitool.dump_sdr(task, sdr_filename)
        res = nm_commands.parse_slave_and_channel(sdr_filename)
    finally:
        ironic_utils.unlink_without_raise(sdr_filename)
    if res is None:
        _save_to_node(False, False)
        raise exception.IPMIFailure(_('Intel Node Manager is not detected.'))
    address, channel = res
    LOG.debug('Intel Node Manager sensors present in SDR on node %(node)s, '
              'channel %(channel)s address %(address)s.',
              {'node': node.uuid, 'channel': channel, 'address': address})
    # SDR can contain wrong info, try simple command
    node.driver_info['ipmi_bridging'] = 'single'
    node.driver_info['ipmi_target_channel'] = channel
    node.driver_info['ipmi_target_address'] = address
    try:
        ipmitool.send_raw(task,
                          _command_to_string(nm_commands.get_version(None)))
        _save_to_node(channel, address)
        return channel, address
    except exception.IPMIFailure:
        _save_to_node(False, False)
        raise exception.IPMIFailure(_('Intel Node Manager sensors record '
                                      'present in SDR but Node Manager is not '
                                      'responding.'))
Beispiel #10
0
def _ipmi_timestamp_to_isotime(timestamp):
    """Convert IPMI timestamp to iso8601."""
    if timestamp == _UNSPECIFIED_TIMESTAMP:
        raise exception.InvalidIPMITimestamp(_('IPMI timestamp is invalid or '
                                               'unspecified'))
    if timestamp <= _INIT_TIMESTAMP_MAX:
        raise exception.InvalidIPMITimestamp(_('IPMI initialization is not '
                                               'completed, relative time is '
                                               '%d second') % timestamp)

    return datetime.datetime.utcfromtimestamp(timestamp).isoformat()
Beispiel #11
0
    def wrapper(raw_data):
        msg = _('Data from Intel Node Manager %s')

        try:
            return func(raw_data)
        except (IndexError, struct.error):
            raise ironic_exception.IPMIFailure(msg % _('has wrong length.'))
        except KeyError:
            raise ironic_exception.IPMIFailure(msg % _('is corrupted.'))
        except ValueError:
            raise ironic_exception.IPMIFailure(msg % _('cannot be converted.'))
    def wrapper(raw_data):
        msg = _('Data from Intel Node Manager %s')

        try:
            return func(raw_data)
        except (IndexError, struct.error):
            raise exception.IPMIFailure(msg % _('has wrong length.'))
        except KeyError:
            raise exception.IPMIFailure(msg % _('is corrupted.'))
        except ValueError:
            raise exception.IPMIFailure(msg % _('cannot be converted.'))
    def set_power_state(self, task, pstate, timeout=None):
        """Turn the power on or off.

        :param task: a TaskManager instance containing the node to act on.
        :param pstate: The desired power state, one of ironic.common.states
            POWER_ON, POWER_OFF.
        :param timeout: timeout (in seconds). Unsupported by this interface.
        :raises: InvalidParameterValue if iboot parameters are invalid or if
            an invalid power state was specified.
        :raises: MissingParameterValue if required iboot parameters are
            missing.
        :raises: PowerStateFailure if the power couldn't be set to pstate.

        """
        # TODO(rloo): Support timeouts!
        if timeout is not None:
            LOG.warning(
                "The 'iboot' Power Interface's 'set_power_state' method "
                "doesn't support the 'timeout' parameter. Ignoring "
                "timeout=%(timeout)s", {'timeout': timeout})

        driver_info = _parse_driver_info(task.node)
        if pstate == states.POWER_ON:
            _switch(driver_info, True)
        elif pstate == states.POWER_OFF:
            _switch(driver_info, False)
        else:
            raise ironic_exception.InvalidParameterValue(
                _("set_power_state called with invalid "
                  "power state %s.") % pstate)

        _check_power_state(driver_info, pstate)
    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 boot of the node.

        :param task: a task from TaskManager.
        :param device: the boot device
        :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.
        """
        node = task.node

        if device not in amt_common.BOOT_DEVICES_MAPPING:
            raise ironic_exception.InvalidParameterValue(
                _("set_boot_device called with invalid device "
                  "%(device)s for node %(node_id)s.") % {
                      'device': device,
                      'node_id': node.uuid
                  })

        # AMT/vPro doesn't support set boot_device persistent, so we have to
        # save amt_boot_device/amt_boot_persistent in driver_internal_info.
        driver_internal_info = node.driver_internal_info
        driver_internal_info['amt_boot_device'] = device
        driver_internal_info['amt_boot_persistent'] = persistent
        node.driver_internal_info = driver_internal_info
        node.save()
    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 boot of the node.

        :param task: a task from TaskManager.
        :param device: the boot device
        :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.
        """
        node = task.node

        if device not in amt_common.BOOT_DEVICES_MAPPING:
            raise ironic_exception.InvalidParameterValue(
                _("set_boot_device called with invalid device "
                  "%(device)s for node %(node_id)s."
                  ) % {'device': device, 'node_id': node.uuid})

        # AMT/vPro doesn't support set boot_device persistent, so we have to
        # save amt_boot_device/amt_boot_persistent in driver_internal_info.
        driver_internal_info = node.driver_internal_info
        driver_internal_info['amt_boot_device'] = device
        driver_internal_info['amt_boot_persistent'] = persistent
        node.driver_internal_info = driver_internal_info
        node.save()
    def set_power_state(self, task, pstate):
        """Wakes the task's node on power on. Powering off is not supported.

        Wakes the task's node on. Wake-On-Lan does not support powering
        the task's node off so, just log it.

        :param task: a TaskManager instance containing the node to act on.
        :param pstate: The desired power state, one of ironic.common.states
            POWER_ON, POWER_OFF.
        :raises: InvalidParameterValue if parameters are invalid.
        :raises: MissingParameterValue if required parameters are missing.
        :raises: WOLOperationError if an error occur when sending the
            magic packets

        """
        node = task.node
        params = _parse_parameters(task)
        if pstate == states.POWER_ON:
            _send_magic_packets(task, params['host'], params['port'])
        elif pstate == states.POWER_OFF:
            LOG.info('Power off called for node %s. Wake-On-Lan does not '
                     'support this operation. Manual intervention '
                     'required to perform this action.', node.uuid)
        else:
            raise ironic_exception.InvalidParameterValue(_(
                "set_power_state called for Node %(node)s with invalid "
                "power state %(pstate)s.") % {'node': node.uuid,
                                              'pstate': pstate})
    def set_power_state(self, task, pstate):
        """Wakes the task's node on power on. Powering off is not supported.

        Wakes the task's node on. Wake-On-Lan does not support powering
        the task's node off so, just log it.

        :param task: a TaskManager instance containing the node to act on.
        :param pstate: The desired power state, one of ironic.common.states
            POWER_ON, POWER_OFF.
        :raises: InvalidParameterValue if parameters are invalid.
        :raises: MissingParameterValue if required parameters are missing.
        :raises: WOLOperationError if an error occur when sending the
            magic packets

        """
        node = task.node
        params = _parse_parameters(task)
        if pstate == states.POWER_ON:
            _send_magic_packets(task, params['host'], params['port'])
        elif pstate == states.POWER_OFF:
            LOG.info(
                _LI('Power off called for node %s. Wake-On-Lan does not '
                    'support this operation. Manual intervention '
                    'required to perform this action.'), node.uuid)
        else:
            raise ironic_exception.InvalidParameterValue(
                _("set_power_state called for Node %(node)s with invalid "
                  "power state %(pstate)s.") % {
                      'node': node.uuid,
                      'pstate': pstate
                  })
    def validate(self, task, method, http_method, **kwargs):
        """Validates the vendor method's parameters.

        This method validates whether the supplied data contains the required
        information for the driver.

        :param task: a TaskManager instance.
        :param method: name of vendor method.
        :param http_method: HTTP method.
        :param kwargs: data passed to vendor's method.
        :raises: InvalidParameterValue if supplied data is not valid.
        :raises: MissingParameterValue if parameters missing in supplied data.
        """
        try:
            if method in ('get_nm_policy', 'remove_nm_policy',
                          'get_nm_policy_suspend', 'remove_nm_policy_suspend'):
                jsonschema.validate(kwargs, self.main_ids_schema)

            elif method == 'control_nm_policy':
                jsonschema.validate(kwargs, self.control_schema)
                no_domain = _('Missing "domain_id"')
                no_policy = _('Missing "policy_id"')
                if kwargs['scope'] == 'domain' and not kwargs.get('domain_id'):
                    raise exception.MissingParameterValue(no_domain)
                if kwargs['scope'] == 'policy':
                    if not kwargs.get('domain_id'):
                        raise exception.MissingParameterValue(no_domain)
                    if not kwargs.get('policy_id'):
                        raise exception.MissingParameterValue(no_policy)

            elif method == 'set_nm_policy':
                jsonschema.validate(kwargs, self.policy_schema)
                if kwargs['policy_trigger'] == 'boot':
                    if not isinstance(kwargs['target_limit'], dict):
                        raise exception.InvalidParameterValue(_('Invalid boot '
                                                                'policy'))

            elif method == 'set_nm_policy_suspend':
                jsonschema.validate(kwargs, self.suspend_schema)

            elif method == 'get_nm_capabilities':
                jsonschema.validate(kwargs, self.get_cap_schema)

        except json_schema_exc.ValidationError as e:
            raise exception.InvalidParameterValue(_('Input data validation '
                                                    'error: %s') % e)
def _set_and_wait(task, target_state):
    """Helper function for DynamicLoopingCall.

    This method changes the power state and polls AMT until the desired
    power state is reached.

    :param task: a TaskManager instance contains the target node.
    :param target_state: desired power state.
    :returns: one of ironic.common.states.
    :raises: PowerStateFailure if cannot set the node to target_state.
    :raises: AMTFailure.
    :raises: AMTConnectFailure
    :raises: InvalidParameterValue
    """
    node = task.node
    driver = task.driver
    if target_state not in (states.POWER_ON, states.POWER_OFF):
        raise ironic_exception.InvalidParameterValue(_(
            'Unsupported target_state: %s') % target_state)
    elif target_state == states.POWER_ON:
        boot_device = node.driver_internal_info.get('amt_boot_device')
        if boot_device and boot_device != amt_common.DEFAULT_BOOT_DEVICE:
            driver.management.ensure_next_boot_device(node, boot_device)

    def _wait(status):
        status['power'] = _power_status(node)
        if status['power'] == target_state:
            raise loopingcall.LoopingCallDone()

        if status['iter'] >= CONF.amt_driver.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_driver.max_attempts})
        status['iter'] += 1

    status = {'power': None, 'iter': 0}

    timer = loopingcall.FixedIntervalLoopingCall(_wait, status)
    timer.start(interval=CONF.amt_driver.action_wait).wait()

    if status['power'] != target_state:
        raise ironic_exception.PowerStateFailure(pstate=target_state)

    return status['power']
Beispiel #20
0
 def __init__(self):
     if not importutils.try_import('pywsman'):
         raise ironic_exception.DriverLoadError(
             driver=self.__class__.__name__,
             reason=_("Unable to import pywsman library"))
     self.power = amt_power.AMTPower()
     self.boot = pxe.PXEBoot()
     self.deploy = agent.AgentDeploy()
     self.management = amt_management.AMTManagement()
 def __init__(self):
     if not importutils.try_import('pywsman'):
         raise ironic_exception.DriverLoadError(
             driver=self.__class__.__name__,
             reason=_("Unable to import pywsman library"))
     self.power = amt_power.AMTPower()
     self.boot = pxe.PXEBoot()
     self.deploy = agent.AgentDeploy()
     self.management = amt_management.AMTManagement()
def validate_network_port(port, port_name="Port"):
    """Validates the given port.

    :param port: TCP/UDP port.
    :param port_name: Name of the port.
    :returns: An integer port number.
    :raises: InvalidParameterValue, if the port is invalid.
    """
    try:
        port = int(port)
    except ValueError:
        raise ironic_exception.InvalidParameterValue(_(
            '%(port_name)s "%(port)s" is not a valid integer.') %
            {'port_name': port_name, 'port': port})
    if port < 1 or port > 65535:
        raise ironic_exception.InvalidParameterValue(_(
            '%(port_name)s "%(port)s" is out of range. Valid port '
            'numbers must be between 1 and 65535.') %
            {'port_name': port_name, 'port': port})
    return port
def _parse_parameters(task):
    driver_info = task.node.driver_info
    host = driver_info.get('wol_host', '255.255.255.255')
    port = driver_info.get('wol_port', 9)
    port = utils.validate_network_port(port, 'wol_port')

    if len(task.ports) < 1:
        raise ironic_exception.MissingParameterValue(_(
            'Wake-On-Lan needs at least one port resource to be '
            'registered in the node'))

    return {'host': host, 'port': port}
def _parse_parameters(task):
    driver_info = task.node.driver_info
    host = driver_info.get('wol_host', '255.255.255.255')
    port = driver_info.get('wol_port', 9)
    port = utils.validate_network_port(port, 'wol_port')

    if len(task.ports) < 1:
        raise ironic_exception.MissingParameterValue(
            _('Wake-On-Lan needs at least one port resource to be '
              'registered in the node'))

    return {'host': host, 'port': port}
Beispiel #25
0
def validate_network_port(port, port_name="Port"):
    """Validates the given port.

    :param port: TCP/UDP port.
    :param port_name: Name of the port.
    :returns: An integer port number.
    :raises: InvalidParameterValue, if the port is invalid.
    """
    try:
        port = int(port)
    except ValueError:
        raise ironic_exception.InvalidParameterValue(
            _('%(port_name)s "%(port)s" is not a valid integer.') % {
                'port_name': port_name,
                'port': port
            })
    if port < 1 or port > 65535:
        raise ironic_exception.InvalidParameterValue(
            _('%(port_name)s "%(port)s" is out of range. Valid port '
              'numbers must be between 1 and 65535.') % {
                  'port_name': port_name,
                  'port': port
              })
    return port
def parse_driver_info(node):
    """Parses and creates AMT driver info

    :param node: an Ironic node object.
    :returns: AMT driver info.
    :raises: MissingParameterValue if any required parameters are missing.
    :raises: InvalidParameterValue if any parameters have invalid values.
    """

    info = node.driver_info or {}
    d_info = {}
    missing_info = []

    for param in REQUIRED_PROPERTIES:
        value = info.get(param)
        if value:
            if isinstance(value, six.text_type):
                value = value.encode()
            d_info[param[4:]] = value
        else:
            missing_info.append(param)

    if missing_info:
        raise ironic_exception.MissingParameterValue(_(
            "AMT driver requires the following to be set in "
            "node's driver_info: %s.") % missing_info)

    d_info['uuid'] = node.uuid
    param = 'amt_protocol'
    protocol = info.get(param, CONF.amt_driver.get(param[4:]))
    if protocol not in AMT_PROTOCOL_PORT_MAP:
        raise ironic_exception.InvalidParameterValue(
            _("Invalid protocol %s.") % protocol)
    d_info[param[4:]] = protocol

    return d_info
    def validate(self, task, method, http_method, **kwargs):
        """Validates the vendor method's parameters.

        This method validates whether the supplied data contains the required
        information for the driver.

        :param task: a TaskManager instance.
        :param method: name of vendor method.
        :param http_method: HTTP method.
        :param kwargs: data passed to vendor's method.
        :raises: InvalidParameterValue if supplied data is not valid.
        :raises: MissingParameterValue if parameters missing in supplied data.
        """
        try:
            if 'statistics' in method:
                self._validate_statistics_methods(method, **kwargs)
            else:
                self._validate_policy_methods(method, **kwargs)
        except json_schema_exc.ValidationError as e:
            raise exception.InvalidParameterValue(_('Input data validation '
                                                    'error: %s') % e)
    def validate(self, task, method, http_method, **kwargs):
        """Validates the vendor method's parameters.

        This method validates whether the supplied data contains the required
        information for the driver.

        :param task: a TaskManager instance.
        :param method: name of vendor method.
        :param http_method: HTTP method.
        :param kwargs: data passed to vendor's method.
        :raises: InvalidParameterValue if supplied data is not valid.
        :raises: MissingParameterValue if parameters missing in supplied data.
        """
        try:
            if 'statistics' in method:
                self._validate_statistics_methods(method, **kwargs)
            else:
                self._validate_policy_methods(method, **kwargs)
        except json_schema_exc.ValidationError as e:
            raise exception.InvalidParameterValue(_('Input data validation '
                                                    'error: %s') % e)
def _send_magic_packets(task, dest_host, dest_port):
    """Create and send magic packets.

    Creates and sends a magic packet for each MAC address registered in
    the Node.

    :param task: a TaskManager instance containing the node to act on.
    :param dest_host: The broadcast to this IP address.
    :param dest_port: The destination port.
    :raises: WOLOperationError if an error occur when connecting to the
        host or sending the magic packets

    """
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    with contextlib.closing(s) as sock:
        for port in task.ports:
            address = port.address.replace(':', '')

            # TODO(lucasagomes): Implement sending the magic packets with
            # SecureON password feature. If your NIC is capable of, you can
            # set the password of your SecureON using the ethtool utility.
            data = 'FFFFFFFFFFFF' + (address * 16)
            packet = bytearray.fromhex(data)

            try:
                sock.sendto(packet, (dest_host, dest_port))
            except socket.error as e:
                msg = (_("Failed to send Wake-On-Lan magic packets to "
                         "node %(node)s port %(port)s. Error: %(error)s") % {
                             'node': task.node.uuid,
                             'port': port.address,
                             'error': e
                         })
                LOG.exception(msg)
                raise exception.WOLOperationError(msg)

            # let's not flood the network with broadcast packets
            time.sleep(0.5)
Beispiel #30
0
    def set_power_state(self, task, pstate, timeout=None):
        """Wakes the task's node on power on. Powering off is not supported.

        Wakes the task's node on. Wake-On-Lan does not support powering
        the task's node off so, just log it.

        :param task: a TaskManager instance containing the node to act on.
        :param pstate: The desired power state, one of ironic.common.states
            POWER_ON, POWER_OFF.
        :param timeout: timeout (in seconds). Unsupported by this interface.
        :raises: InvalidParameterValue if parameters are invalid.
        :raises: MissingParameterValue if required parameters are missing.
        :raises: WOLOperationError if an error occur when sending the
            magic packets

        """
        # TODO(rloo): Support timeouts!
        if timeout is not None:
            LOG.warning(
                "The 'wol' Power Interface's 'set_power_state' method "
                "doesn't support the 'timeout' parameter. Ignoring "
                "timeout=%(timeout)s",
                {'timeout': timeout})

        node = task.node
        params = _parse_parameters(task)
        if pstate == states.POWER_ON:
            _send_magic_packets(task, params['host'], params['port'])
        elif pstate == states.POWER_OFF:
            LOG.info('Power off called for node %s. Wake-On-Lan does not '
                     'support this operation. Manual intervention '
                     'required to perform this action.', node.uuid)
        else:
            raise ironic_exception.InvalidParameterValue(_(
                "set_power_state called for Node %(node)s with invalid "
                "power state %(pstate)s.") % {'node': node.uuid,
                                              'pstate': pstate})
def _send_magic_packets(task, dest_host, dest_port):
    """Create and send magic packets.

    Creates and sends a magic packet for each MAC address registered in
    the Node.

    :param task: a TaskManager instance containing the node to act on.
    :param dest_host: The broadcast to this IP address.
    :param dest_port: The destination port.
    :raises: WOLOperationError if an error occur when connecting to the
        host or sending the magic packets

    """
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    with contextlib.closing(s) as sock:
        for port in task.ports:
            address = port.address.replace(':', '')

            # TODO(lucasagomes): Implement sending the magic packets with
            # SecureON password feature. If your NIC is capable of, you can
            # set the password of your SecureON using the ethtool utility.
            data = 'FFFFFFFFFFFF' + (address * 16)
            packet = bytearray.fromhex(data)

            try:
                sock.sendto(packet, (dest_host, dest_port))
            except socket.error as e:
                msg = (_("Failed to send Wake-On-Lan magic packets to "
                         "node %(node)s port %(port)s. Error: %(error)s") %
                       {'node': task.node.uuid, 'port': port.address,
                        'error': e})
                LOG.exception(msg)
                raise exception.WOLOperationError(msg)

            # let's not flood the network with broadcast packets
            time.sleep(0.5)
Beispiel #32
0
    def set_power_state(self, task, pstate):
        """Turn the power on or off.

        :param task: a TaskManager instance containing the node to act on.
        :param pstate: The desired power state, one of ironic.common.states
            POWER_ON, POWER_OFF.
        :raises: InvalidParameterValue if iboot parameters are invalid or if
            an invalid power state was specified.
        :raises: MissingParameterValue if required iboot parameters are
            missing.
        :raises: PowerStateFailure if the power couldn't be set to pstate.

        """
        driver_info = _parse_driver_info(task.node)
        if pstate == states.POWER_ON:
            _switch(driver_info, True)
        elif pstate == states.POWER_OFF:
            _switch(driver_info, False)
        else:
            raise ironic_exception.InvalidParameterValue(
                _("set_power_state called with invalid "
                  "power state %s.") % pstate)

        _check_power_state(driver_info, pstate)
    def set_power_state(self, task, pstate):
        """Turn the power on or off.

        :param task: a TaskManager instance containing the node to act on.
        :param pstate: The desired power state, one of ironic.common.states
            POWER_ON, POWER_OFF.
        :raises: InvalidParameterValue if iboot parameters are invalid or if
            an invalid power state was specified.
        :raises: MissingParameterValue if required iboot parameters are
            missing.
        :raises: PowerStateFailure if the power couldn't be set to pstate.

        """
        driver_info = _parse_driver_info(task.node)
        if pstate == states.POWER_ON:
            _switch(driver_info, True)
        elif pstate == states.POWER_OFF:
            _switch(driver_info, False)
        else:
            raise ironic_exception.InvalidParameterValue(
                _("set_power_state called with invalid "
                  "power state %s.") % pstate)

        _check_power_state(driver_info, pstate)
def _set_and_wait(task, target_state):
    """Helper function for DynamicLoopingCall.

    This method changes the power state and polls AMT until the desired
    power state is reached.

    :param task: a TaskManager instance contains the target node.
    :param target_state: desired power state.
    :returns: one of ironic.common.states.
    :raises: PowerStateFailure if cannot set the node to target_state.
    :raises: AMTFailure.
    :raises: AMTConnectFailure
    :raises: InvalidParameterValue
    """
    node = task.node
    driver = task.driver
    if target_state not in (states.POWER_ON, states.POWER_OFF):
        raise ironic_exception.InvalidParameterValue(
            _('Unsupported target_state: %s') % target_state)
    elif target_state == states.POWER_ON:
        boot_device = node.driver_internal_info.get('amt_boot_device')
        if boot_device and boot_device != amt_common.DEFAULT_BOOT_DEVICE:
            driver.management.ensure_next_boot_device(node, boot_device)

    def _wait(status):
        status['power'] = _power_status(node)
        if status['power'] == target_state:
            raise loopingcall.LoopingCallDone()

        if status['iter'] >= CONF.amt_driver.max_attempts:
            status['power'] = states.ERROR
            LOG.warning(
                "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(
                "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_driver.max_attempts
                })
        status['iter'] += 1

    status = {'power': None, 'iter': 0}

    timer = loopingcall.FixedIntervalLoopingCall(_wait, status)
    timer.start(interval=CONF.amt_driver.action_wait).wait()

    if status['power'] != target_state:
        raise ironic_exception.PowerStateFailure(pstate=target_state)

    return status['power']
from oslo_log import log as logging
from oslo_service import loopingcall
from oslo_utils import excutils
from oslo_utils import importutils

from ironic_staging_drivers.amt import common as amt_common
from ironic_staging_drivers.amt import resource_uris
from ironic_staging_drivers.common import exception
from ironic_staging_drivers.common.i18n import _

pywsman = importutils.try_import('pywsman')

opts = [
    cfg.IntOpt('max_attempts',
               default=3,
               help=_('Maximum number of times to attempt an AMT operation, '
                      'before failing')),
    cfg.IntOpt('action_wait',
               default=10,
               help=_('Amount of time (in seconds) to wait, before retrying '
                      'an AMT operation'))
]

CONF = cfg.CONF
CONF.register_opts(opts, group='amt_driver')

LOG = logging.getLogger(__name__)

AMT_POWER_MAP = {
    states.POWER_ON: '2',
    states.POWER_OFF: '8',
}
from oslo_service import loopingcall
from oslo_utils import importutils
import six

from ironic_staging_drivers.common.i18n import _
from ironic_staging_drivers.common import utils

iboot = importutils.try_import('iboot')

LOG = logging.getLogger(__name__)

opts = [
    cfg.IntOpt('max_retry',
               default=3,
               min=0,
               help=_('Maximum retries for iBoot operations')),
    cfg.IntOpt('retry_interval',
               default=1,
               min=0,
               help=_('Time (in seconds) between retry attempts for iBoot '
                      'operations')),
    cfg.IntOpt('reboot_delay',
               default=5,
               min=0,
               help=_('Time (in seconds) to sleep between when rebooting '
                      '(powering off and on again).'))
]

CONF = cfg.CONF
opt_group = cfg.OptGroup(name='iboot',
                         title='Options for the iBoot power driver')
class AMTConnectFailure(exception.IronicException):
    _msg_fmt = _("Failed to connect to AMT service. This could be caused "
                 "by the wrong amt_address or bad network environment.")
class AMTFailure(exception.IronicException):
    _msg_fmt = _("AMT call failed: %(cmd)s.")
class LibvirtError(exception.IronicException):
    message = _("Libvirt call failed: %(err)s.")
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import importutils
import six

from ironic_staging_drivers.common import exception
from ironic_staging_drivers.common.i18n import _

pywsman = importutils.try_import('pywsman')

_SOAP_ENVELOPE = 'http://www.w3.org/2003/05/soap-envelope'

LOG = logging.getLogger(__name__)

REQUIRED_PROPERTIES = {
    'amt_address': _('IP address or host name of the node. Required.'),
    'amt_password': _('Password. Required.'),
    'amt_username': _('Username to log into AMT system. Required.'),
}
OPTIONAL_PROPERTIES = {
    'amt_protocol': _('Protocol used for AMT endpoint. one of http, https; '
                      'default is "http". Optional.'),
}
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)

opts = [
    cfg.StrOpt('protocol',
               default='http',
               choices=['http', 'https'],
               help=_('Protocol used for AMT endpoint, '
from oslo_service import loopingcall
from oslo_utils import importutils
import six

from ironic_staging_drivers.common.i18n import _
from ironic_staging_drivers.common import utils

iboot = importutils.try_import('iboot')

LOG = logging.getLogger(__name__)

opts = [
    cfg.IntOpt('max_retry',
               default=3,
               min=0,
               help=_('Maximum retries for iBoot operations')),
    cfg.IntOpt('retry_interval',
               default=1,
               min=0,
               help=_('Time (in seconds) between retry attempts for iBoot '
                      'operations')),
    cfg.IntOpt('reboot_delay',
               default=5,
               min=0,
               help=_('Time (in seconds) to sleep between when rebooting '
                      '(powering off and on again).'))
]

CONF = cfg.CONF
opt_group = cfg.OptGroup(name='iboot',
                         title='Options for the iBoot power driver')
from ironic.common import states
from ironic.conductor import task_manager
from ironic.drivers import base
from oslo_log import log

from ironic_staging_drivers.common import exception
from ironic_staging_drivers.common.i18n import _
from ironic_staging_drivers.common.i18n import _LI
from ironic_staging_drivers.common import utils

LOG = log.getLogger(__name__)

REQUIRED_PROPERTIES = {}
OPTIONAL_PROPERTIES = {
    'wol_host':
    _('Broadcast IP address; defaults to '
      '255.255.255.255. Optional.'),
    'wol_port':
    _("Destination port; defaults to 9. Optional."),
}
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)


def _send_magic_packets(task, dest_host, dest_port):
    """Create and send magic packets.

    Creates and sends a magic packet for each MAC address registered in
    the Node.

    :param task: a TaskManager instance containing the node to act on.
    :param dest_host: The broadcast to this IP address.
from ironic.common import exception as ironic_exception
from ironic.common import states
from ironic.conductor import task_manager
from ironic.drivers import base
from oslo_log import log

from ironic_staging_drivers.common import exception
from ironic_staging_drivers.common.i18n import _
from ironic_staging_drivers.common import utils


LOG = log.getLogger(__name__)

REQUIRED_PROPERTIES = {}
OPTIONAL_PROPERTIES = {
    'wol_host': _('Broadcast IP address; defaults to '
                  '255.255.255.255. Optional.'),
    'wol_port': _("Destination port; defaults to 9. Optional."),
}
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)


def _send_magic_packets(task, dest_host, dest_port):
    """Create and send magic packets.

    Creates and sends a magic packet for each MAC address registered in
    the Node.

    :param task: a TaskManager instance containing the node to act on.
    :param dest_host: The broadcast to this IP address.
    :param dest_port: The destination port.
from oslo_utils import importutils

from ironic_staging_drivers.amt import common as amt_common
from ironic_staging_drivers.amt import resource_uris
from ironic_staging_drivers.common import exception
from ironic_staging_drivers.common.i18n import _
from ironic_staging_drivers.common.i18n import _LE
from ironic_staging_drivers.common.i18n import _LI
from ironic_staging_drivers.common.i18n import _LW

pywsman = importutils.try_import('pywsman')

opts = [
    cfg.IntOpt('max_attempts',
               default=3,
               help=_('Maximum number of times to attempt an AMT operation, '
                      'before failing')),
    cfg.IntOpt('action_wait',
               default=10,
               help=_('Amount of time (in seconds) to wait, before retrying '
                      'an AMT operation'))
]

CONF = cfg.CONF
CONF.register_opts(opts, group='amt_driver')

LOG = logging.getLogger(__name__)

AMT_POWER_MAP = {
    states.POWER_ON: '2',
    states.POWER_OFF: '8',
}