Example #1
0
    def _heartbeat_no_uuid(self, context, **kwargs):
        """Method to be called the first time a ramdisk agent checks in. This
        can be because this is a node just entering decom or a node that
        rebooted for some reason. We will use the mac addresses listed in the
        kwargs to find the matching node, then return the node object to the
        agent. The agent can that use that UUID to use the normal vendor
        passthru method.

        Currently, we don't handle the instance where the agent doesn't have
        a matching node (i.e. a brand new, never been in Ironic node).

        kwargs should have the following format:
        {
            hardware: [
                {
                    'id': 'aa:bb:cc:dd:ee:ff',
                    'type': 'mac_address'
                },
                {
                    'id': '00:11:22:33:44:55',
                    'type': 'mac_address'
                }
            ], ...
        }

        hardware is a list of dicts with id being the actual mac address,
        with type 'mac_address' for the non-IPMI ports in the
        server, (the normal network ports). They should be in the format
        "aa:bb:cc:dd:ee:ff".

        This method will also return the timeout for heartbeats. The driver
        will expect the agent to heartbeat before that timeout, or it will be
        considered down. This will be in a root level key called
        'heartbeat_timeout'
        """
        if 'hardware' not in kwargs or not kwargs['hardware']:
            raise exception.InvalidParameterValue('"hardware" is a '
                                                  'required parameter and must'
                                                  ' not be empty')

        # Find the address from the hardware list
        mac_addresses = []
        for hardware in kwargs['hardware']:
            if 'id' not in hardware or 'type' not in hardware:
                self.LOG.warning(_('Malformed hardware entry %s') % hardware)
                continue
            if hardware['type'] == 'mac_address':
                try:
                    mac = utils.validate_and_normalize_mac(hardware['id'])
                except exception.InvalidMAC:
                    self.LOG.warning(_('Malformed MAC in hardware entry %s.')
                                     % hardware)
                    continue
                mac_addresses.append(mac)

        node_object = self._find_node_by_macs(context, mac_addresses)
        return {
            'heartbeat_timeout': CONF.teeth_driver.heartbeat_timeout,
            'node': node_object
        }
Example #2
0
 def test_validate_and_normalize_mac(self):
     mac = 'AA:BB:CC:DD:EE:FF'
     with mock.patch.object(netutils, 'is_valid_mac',
                            autospec=True) as m_mock:
         m_mock.return_value = True
         self.assertEqual(mac.lower(),
                          utils.validate_and_normalize_mac(mac))
Example #3
0
 def test_validate_and_normalize_mac(self):
     mac = 'AA:BB:CC:DD:EE:FF'
     with mock.patch.object(netutils, 'is_valid_mac',
                            autospec=True) as m_mock:
         m_mock.return_value = True
         self.assertEqual(mac.lower(),
                          utils.validate_and_normalize_mac(mac))
Example #4
0
    def _get_mac_addresses(self, interfaces):
        """Returns MACs for the network devices."""
        mac_addresses = []

        for interface in interfaces:
            try:
                mac_addresses.append(utils.validate_and_normalize_mac(interface.get("mac_address")))
            except exception.InvalidMAC:
                LOG.warning(_LW("Malformed MAC: %s"), interface.get("mac_address"))
        return mac_addresses
Example #5
0
    def _get_mac_addresses(self, interfaces):
        """Returns MACs for the network devices."""
        mac_addresses = []

        for interface in interfaces:
            try:
                mac_addresses.append(utils.validate_and_normalize_mac(
                    interface.get('mac_address')))
            except exception.InvalidMAC:
                LOG.warning(_LW('Malformed MAC: %s'), interface.get(
                    'mac_address'))
        return mac_addresses
Example #6
0
    def validate(value):
        """Validate and convert the input to a LocalLinkConnectionType.

        :param value: A dictionary of values to validate, switch_id is a MAC
            address or an OpenFlow based datapath_id, switch_info is an
            optional field.

        For example::

         {
            'switch_id': mac_or_datapath_id(),
            'port_id': 'Ethernet3/1',
            'switch_info': 'switch1'
         }

        :returns: A dictionary.
        :raises: Invalid if some of the keys in the dictionary being validated
            are unknown, invalid, or some required ones are missing.
        """
        wtypes.DictType(wtypes.text, wtypes.text).validate(value)

        keys = set(value)

        # This is to workaround an issue when an API object is initialized from
        # RPC object, in which dictionary fields that are set to None become
        # empty dictionaries
        if not keys:
            return value

        invalid = keys - LocalLinkConnectionType.valid_fields
        if invalid:
            raise exception.Invalid(_('%s are invalid keys') % (invalid))

        # Check all mandatory fields are present
        missing = LocalLinkConnectionType.mandatory_fields - keys
        if missing:
            msg = _('Missing mandatory keys: %s') % missing
            raise exception.Invalid(msg)

        # Check switch_id is either a valid mac address or
        # OpenFlow datapath_id and normalize it.
        try:
            value['switch_id'] = utils.validate_and_normalize_mac(
                value['switch_id'])
        except exception.InvalidMAC:
            try:
                value['switch_id'] = utils.validate_and_normalize_datapath_id(
                    value['switch_id'])
            except exception.InvalidDatapathID:
                raise exception.InvalidSwitchID(switch_id=value['switch_id'])

        return value
Example #7
0
    def validate(value):
        """Validate and convert the input to a LocalLinkConnectionType.

        :param value: A dictionary of values to validate, switch_id is a MAC
            address or an OpenFlow based datapath_id, switch_info is an
            optional field.

        For example::

         {
            'switch_id': mac_or_datapath_id(),
            'port_id': 'Ethernet3/1',
            'switch_info': 'switch1'
         }

        :returns: A dictionary.
        :raises: Invalid if some of the keys in the dictionary being validated
            are unknown, invalid, or some required ones are missing.
        """
        wtypes.DictType(wtypes.text, wtypes.text).validate(value)

        keys = set(value)

        # This is to workaround an issue when an API object is initialized from
        # RPC object, in which dictionary fields that are set to None become
        # empty dictionaries
        if not keys:
            return value

        invalid = keys - LocalLinkConnectionType.valid_fields
        if invalid:
            raise exception.Invalid(_('%s are invalid keys') % (invalid))

        # Check all mandatory fields are present
        missing = LocalLinkConnectionType.mandatory_fields - keys
        if missing:
            msg = _('Missing mandatory keys: %s') % missing
            raise exception.Invalid(msg)

        # Check switch_id is either a valid mac address or
        # OpenFlow datapath_id and normalize it.
        try:
            value['switch_id'] = utils.validate_and_normalize_mac(
                value['switch_id'])
        except exception.InvalidMAC:
            try:
                value['switch_id'] = utils.validate_and_normalize_datapath_id(
                    value['switch_id'])
            except exception.InvalidDatapathID:
                raise exception.InvalidSwitchID(switch_id=value['switch_id'])

        return value
    def _get_node_by_mac(self, context, mac_addresses):
        for mac in mac_addresses:
            try:
                mac = utils.validate_and_normalize_mac(mac)
            except exception.InvalidMAC:
                LOG.warning('Incorrect MAC address: %s', mac)

            try:
                port = objects.Port.get_by_address(context, mac)
                return objects.Node.get_by_id(context, port.node_id)
            except exception.PortNotFound:
                continue

        raise exception.NodeNotFound(_(
            'No nodes with any of these MAC addresses: %s') % mac_addresses)
    def _create_ports(self, context, node, interfaces):
        for interface in interfaces:
            try:
                address = utils.validate_and_normalize_mac(
                    interface['mac_address']
                )
            except exception.InvalidMAC:
                LOG.warning('Cannot create port for MAC address: %s', address)
                continue

            port = objects.Port(
                node_id=node.id,
                address=address,
            )
            port.create()
Example #10
0
def mac_address(name, value):
    """Validate that the value represents a MAC address

    :param name: Name of the argument
    :param value: A string value representing a MAC address
    :returns: The value as a normalized MAC address, or None if value is None
    :raises: InvalidParameterValue if the value is not a valid MAC address
    """
    if value is None:
        return
    try:
        return utils.validate_and_normalize_mac(value)
    except exception.InvalidMAC:
        raise exception.InvalidParameterValue(
            _('Expected valid MAC address for %s: %s') % (name, value))
Example #11
0
 def validate(value):
     return utils.validate_and_normalize_mac(value)
Example #12
0
    def get_all(self, addresses=None, node_uuid=None):
        """Look up a node by its MAC addresses and optionally UUID.

        If the "restrict_lookup" option is set to True (the default), limit
        the search to nodes in certain transient states (e.g. deploy wait).

        :param addresses: list of MAC addresses for a node.
        :param node_uuid: UUID of a node.
        :raises: NotFound if requested API version does not allow this
            endpoint.
        :raises: NotFound if suitable node was not found or node's provision
            state is not allowed for the lookup.
        :raises: IncompleteLookup if neither node UUID nor any valid MAC
            address was provided.
        """
        if not api_utils.allow_ramdisk_endpoints():
            raise exception.NotFound()

        cdict = pecan.request.context.to_policy_values()
        policy.authorize('baremetal:driver:ipa_lookup', cdict, cdict)

        # Validate the list of MAC addresses
        if addresses is None:
            addresses = []

        valid_addresses = []
        invalid_addresses = []
        for addr in addresses:
            try:
                mac = utils.validate_and_normalize_mac(addr)
                valid_addresses.append(mac)
            except exception.InvalidMAC:
                invalid_addresses.append(addr)

        if invalid_addresses:
            node_log = ('' if not node_uuid else '(Node UUID: %s)' % node_uuid)
            LOG.warning(
                'The following MAC addresses "%(addrs)s" are '
                'invalid and will be ignored by the lookup '
                'request %(node)s', {
                    'addrs': ', '.join(invalid_addresses),
                    'node': node_log
                })

        if not valid_addresses and not node_uuid:
            raise exception.IncompleteLookup()

        try:
            if node_uuid:
                node = objects.Node.get_by_uuid(pecan.request.context,
                                                node_uuid)
            else:
                node = objects.Node.get_by_port_addresses(
                    pecan.request.context, valid_addresses)
        except exception.NotFound:
            # NOTE(dtantsur): we are reraising the same exception to make sure
            # we don't disclose the difference between nodes that are not found
            # at all and nodes in a wrong state by different error messages.
            raise exception.NotFound()

        if (CONF.api.restrict_lookup
                and node.provision_state not in _LOOKUP_ALLOWED_STATES):
            raise exception.NotFound()

        return LookupResult.convert_with_links(node)
Example #13
0
    def get_all(self, addresses=None, node_uuid=None):
        """Look up a node by its MAC addresses and optionally UUID.

        If the "restrict_lookup" option is set to True (the default), limit
        the search to nodes in certain transient states (e.g. deploy wait).

        :param addresses: list of MAC addresses for a node.
        :param node_uuid: UUID of a node.
        :raises: NotFound if requested API version does not allow this
            endpoint.
        :raises: NotFound if suitable node was not found.
        """
        if not api_utils.allow_ramdisk_endpoints():
            raise exception.NotFound()

        cdict = pecan.request.context.to_dict()
        policy.authorize('baremetal:driver:ipa_lookup', cdict, cdict)

        # Validate the list of MAC addresses
        if addresses is None:
            addresses = []

        valid_addresses = []
        invalid_addresses = []
        for addr in addresses:
            try:
                mac = utils.validate_and_normalize_mac(addr)
                valid_addresses.append(mac)
            except exception.InvalidMAC:
                invalid_addresses.append(addr)

        if invalid_addresses:
            node_log = ('' if not node_uuid
                        else _LW('(Node UUID: %s)') % node_uuid)
            LOG.warning(_LW('The following MAC addresses "%(addrs)s" are '
                            'invalid and will be ignored by the lookup '
                            'request %(node)s'),
                        {'addrs': ', '.join(invalid_addresses),
                         'node': node_log})

        if not valid_addresses and not node_uuid:
            raise exception.IncompleteLookup()

        try:
            if node_uuid:
                node = objects.Node.get_by_uuid(
                    pecan.request.context, node_uuid)
            else:
                node = objects.Node.get_by_port_addresses(
                    pecan.request.context, valid_addresses)
        except exception.NotFound:
            # NOTE(dtantsur): we are reraising the same exception to make sure
            # we don't disclose the difference between nodes that are not found
            # at all and nodes in a wrong state by different error messages.
            raise exception.NotFound()

        if (CONF.api.restrict_lookup and
                node.provision_state not in _LOOKUP_ALLOWED_STATES):
            raise exception.NotFound()

        return LookupResult.convert_with_links(node)
Example #14
0
 def validate(value):
     return utils.validate_and_normalize_mac(value)
Example #15
0
    def validate(value):
        """Validate and convert the input to a LocalLinkConnectionType.

        :param value: A dictionary of values to validate, switch_id is a MAC
            address or an OpenFlow based datapath_id, switch_info is an
            optional field. Required Smart NIC fields are port_id and hostname.

        For example::

         {
            'switch_id': mac_or_datapath_id(),
            'port_id': 'Ethernet3/1',
            'switch_info': 'switch1'
         }

        Or for Smart NIC::

         {
            'port_id': 'rep0-0',
            'hostname': 'host1-bf'
         }

        :returns: A dictionary.
        :raises: Invalid if some of the keys in the dictionary being validated
            are unknown, invalid, or some required ones are missing.
        """
        wtypes.DictType(wtypes.text, wtypes.text).validate(value)

        keys = set(value)

        # This is to workaround an issue when an API object is initialized from
        # RPC object, in which dictionary fields that are set to None become
        # empty dictionaries
        if not keys:
            return value

        invalid = keys - LocalLinkConnectionType.valid_fields
        if invalid:
            raise exception.Invalid(_('%s are invalid keys') % (invalid))

        # Check any mandatory fields sets are present
        for mandatory_set in LocalLinkConnectionType.mandatory_fields_list:
            if mandatory_set <= keys:
                break
        else:
            msg = _('Missing mandatory keys. Required keys are '
                    '%(required_fields)s. Or in case of Smart NIC '
                    '%(smart_nic_required_fields)s. '
                    'Submitted keys are %(keys)s .') % {
                        'required_fields':
                        LocalLinkConnectionType.local_link_mandatory_fields,
                        'smart_nic_required_fields':
                        LocalLinkConnectionType.smart_nic_mandatory_fields,
                        'keys': keys
                    }
            raise exception.Invalid(msg)

        # Check switch_id is either a valid mac address or
        # OpenFlow datapath_id and normalize it.
        try:
            value['switch_id'] = utils.validate_and_normalize_mac(
                value['switch_id'])
        except exception.InvalidMAC:
            try:
                value['switch_id'] = utils.validate_and_normalize_datapath_id(
                    value['switch_id'])
            except exception.InvalidDatapathID:
                raise exception.InvalidSwitchID(switch_id=value['switch_id'])
        except KeyError:
            # In Smart NIC case 'switch_id' is optional.
            pass

        return value
Example #16
0
 def coerce(obj, attr, value):
     return utils.validate_and_normalize_mac(value)
Example #17
0
 def coerce(obj, attr, value):
     return utils.validate_and_normalize_mac(value)