Exemple #1
0
def _verify_security_groups(security_groups, client):
    """Verify that the security groups exist.

    :param security_groups: a list of security group UUIDs; may be None or
        empty
    :param client: Neutron client
    :raises: NetworkError
    """

    if not security_groups:
        return
    try:
        neutron_sec_groups = (
            client.list_security_groups(id=security_groups, fields='id').get(
                'security_groups', []))
    except neutron_exceptions.NeutronClientException as e:
        msg = (_("Could not retrieve security groups from neutron: %(exc)s") %
               {'exc': e})
        LOG.exception(msg)
        raise exception.NetworkError(msg)

    if set(security_groups).issubset(x['id'] for x in neutron_sec_groups):
        return

    missing_sec_groups = set(security_groups).difference(
        x['id'] for x in neutron_sec_groups)
    msg = (_('Could not find these security groups (specified via ironic '
             'config) in neutron: %(ir-sg)s')
           % {'ir-sg': list(missing_sec_groups)})
    LOG.error(msg)
    raise exception.NetworkError(msg)
Exemple #2
0
def remove_neutron_ports(task, params):
    """Deletes the neutron ports matched by params.

    :param task: a TaskManager instance.
    :param params: Dict of params to filter ports.
    :raises: NetworkError
    """
    client = get_client(context=task.context)
    node_uuid = task.node.uuid

    try:
        response = client.list_ports(**params)
    except neutron_exceptions.NeutronClientException as e:
        msg = (_('Could not get given network VIF for %(node)s '
                 'from neutron, possible network issue. %(exc)s') % {
                     'node': node_uuid,
                     'exc': e
                 })
        LOG.exception(msg)
        raise exception.NetworkError(msg)

    ports = response.get('ports', [])
    if not ports:
        LOG.debug('No ports to remove for node %s', node_uuid)
        return

    for port in ports:
        LOG.debug(
            'Deleting neutron port %(vif_port_id)s of node '
            '%(node_id)s.', {
                'vif_port_id': port['id'],
                'node_id': node_uuid
            })

        if is_smartnic_port(port):
            wait_for_host_agent(client, port['binding:host_id'])
        try:
            client.delete_port(port['id'])
        # NOTE(mgoddard): Ignore if the port was deleted by nova.
        except neutron_exceptions.PortNotFoundClient:
            LOG.info('Port %s was not found while deleting.', port['id'])
        except neutron_exceptions.NeutronClientException as e:
            msg = (_('Could not remove VIF %(vif)s of node %(node)s, possibly '
                     'a network issue: %(exc)s') % {
                         'vif': port['id'],
                         'node': node_uuid,
                         'exc': e
                     })
            LOG.exception(msg)
            raise exception.NetworkError(msg)

    LOG.info('Successfully removed node %(node_uuid)s neutron ports.',
             {'node_uuid': node_uuid})
Exemple #3
0
def remove_neutron_ports(task, params):
    """Deletes the neutron ports matched by params.

    :param task: a TaskManager instance.
    :param params: Dict of params to filter ports.
    :raises: NetworkError
    """
    client = get_client()
    node_uuid = task.node.uuid

    try:
        response = client.list_ports(**params)
    except neutron_exceptions.NeutronClientException as e:
        msg = (_('Could not get given network VIF for %(node)s '
                 'from neutron, possible network issue. %(exc)s') % {
                     'node': node_uuid,
                     'exc': e
                 })
        LOG.exception(msg)
        raise exception.NetworkError(msg)

    ports = response.get('ports', [])
    if not ports:
        LOG.debug('No ports to remove for node %s', node_uuid)
        return

    for port in ports:
        LOG.debug(
            'Deleting neutron port %(vif_port_id)s of node '
            '%(node_id)s.', {
                'vif_port_id': port['id'],
                'node_id': node_uuid
            })

        try:
            client.delete_port(port['id'])
        except neutron_exceptions.NeutronClientException as e:
            msg = (_('Could not remove VIF %(vif)s of node %(node)s, possibly '
                     'a network issue: %(exc)s') % {
                         'vif': port['id'],
                         'node': node_uuid,
                         'exc': e
                     })
            LOG.exception(msg)
            raise exception.NetworkError(msg)

    LOG.info(_LI('Successfully removed node %(node_uuid)s neutron ports.'),
             {'node_uuid': node_uuid})
Exemple #4
0
def unbind_neutron_port(port_id, client=None):
    """Unbind a neutron port

    Remove a neutron port's binding profile and host ID so that it returns to
    an unbound state.

    :param port_id: Neutron port ID.
    :param client: Optional a Neutron client object.
    :raises: NetworkError
    """

    if not client:
        client = get_client()

    body = {'port': {'binding:host_id': '',
                     'binding:profile': {}}}

    try:
        client.update_port(port_id, body)
    # NOTE(vsaienko): Ignore if port was deleted before calling vif detach.
    except neutron_exceptions.PortNotFoundClient:
        LOG.info('Port %s was not found while unbinding.', port_id)
    except neutron_exceptions.NeutronClientException as e:
        msg = (_('Unable to clear binding profile for '
                 'neutron port %(port_id)s. Error: '
                 '%(err)s') % {'port_id': port_id, 'err': e})
        LOG.exception(msg)
        raise exception.NetworkError(msg)
Exemple #5
0
def unbind_neutron_port(port_id, client=None, context=None):
    """Unbind a neutron port

    Remove a neutron port's binding profile and host ID so that it returns to
    an unbound state.

    :param port_id: Neutron port ID.
    :param client: Optional a Neutron client object.
    :param context: request context
    :type context: ironic.common.context.RequestContext
    :raises: NetworkError
    """

    body_unbind = {'port': {'binding:host_id': '',
                            'binding:profile': {}}}
    body_reset_mac = {'port': {'mac_address': None}}

    try:
        update_neutron_port(context, port_id, body_unbind, client)
        # NOTE(hjensas): We need to reset the mac address in a separate step.
        #   Exception PortBound will be raised by neutron as it refuses to
        #   update the mac address of a bound port if we attempt to unbind and
        #   reset the mac in the same call.
        update_neutron_port(context, port_id, body_reset_mac, client)
    # NOTE(vsaienko): Ignore if port was deleted before calling vif detach.
    except neutron_exceptions.PortNotFoundClient:
        LOG.info('Port %s was not found while unbinding.', port_id)
    except neutron_exceptions.NeutronClientException as e:
        msg = (_('Unable to clear binding profile for '
                 'neutron port %(port_id)s. Error: '
                 '%(err)s') % {'port_id': port_id, 'err': e})
        LOG.exception(msg)
        raise exception.NetworkError(msg)
Exemple #6
0
def _get_network_by_uuid_or_name(client, uuid_or_name, net_type=_('network')):
    """Return a neutron network by UUID or name.

    :param client: A Neutron client object.
    :param uuid_or_name: network UUID or name
    :param net_type: human-readable network type for error messages
    :returns: A dict describing the neutron network.
    :raises: NetworkError on failure to contact Neutron
    :raises: InvalidParameterValue for missing or duplicated network
    """

    try:
        network = client.find_network(uuid_or_name, ignore_missing=False)
    except openstack_exc.DuplicateResource:
        network_ids = [net.id for net in client.networks(name=uuid_or_name)]
        raise exception.InvalidParameterValue(
            _('More than one %(type)s was found for name %(name)s: %(nets)s') %
            {'name': uuid_or_name, 'nets': ', '.join(network_ids),
             'type': net_type})
    except openstack_exc.ResourceNotFound:
        raise exception.InvalidParameterValue(
            _('%(type)s with name or UUID %(uuid_or_name)s was not found') %
            {'type': net_type, 'uuid_or_name': uuid_or_name})
    except openstack_exc.OpenStackCloudException as exc:
        raise exception.NetworkError(_('Could not retrieve network: %s') % exc)

    LOG.debug('Got network matching %(uuid_or_name)s: %(result)s',
              {'uuid_or_name': uuid_or_name, 'result': network})

    return network
Exemple #7
0
def unbind_neutron_port(port_id, client=None):
    """Unbind a neutron port

    Remove a neutron port's binding profile and host ID so that it returns to
    an unbound state.

    :param port_id: Neutron port ID.
    :param client: Optional a Neutron client object.
    :raises: NetworkError
    """

    if not client:
        client = get_client()

    body = {'port': {'binding:host_id': '',
                     'binding:profile': {}}}

    try:
        client.update_port(port_id, body)
    except neutron_exceptions.NeutronClientException as e:
        msg = (_('Unable to clear binding profile for '
                 'neutron port %(port_id)s. Error: '
                 '%(err)s') % {'port_id': port_id, 'err': e})
        LOG.exception(msg)
        raise exception.NetworkError(msg)
Exemple #8
0
def wait_for_host_agent(client, host_id, target_state='up'):
    """Wait for neutron agent to become target state

    :param client: A Neutron client object.
    :param host_id: Agent host_id
    :param target_state: up: wait for up status,
        down: wait for down status
    :returns: boolean indicates the agent state matches
        param value target_state_up.
    :raises: exception.Invalid if 'target_state' is not valid.
    :raises: exception.NetworkError if host status didn't match the required
        status after max retry attempts.
    """
    if target_state not in ['up', 'down']:
        raise exception.Invalid(
            'Invalid requested agent state to validate, accepted values: '
            'up, down. Requested state: %(target_state)s' % {
                'target_state': target_state})

    LOG.debug('Validating host %(host_id)s agent is %(status)s',
              {'host_id': host_id,
               'status': target_state})
    is_alive = _validate_agent(client, host=host_id)
    LOG.debug('Agent on host %(host_id)s is %(status)s',
              {'host_id': host_id,
               'status': 'up' if is_alive else 'down'})
    if ((target_state == 'up' and is_alive)
            or (target_state == 'down' and not is_alive)):
        return True
    raise exception.NetworkError(
        'Agent on host %(host)s failed to reach state %(state)s' % {
            'host': host_id, 'state': target_state})
Exemple #9
0
 def _bind_flat_ports(self, task):
     LOG.debug("Binding flat network ports")
     client = neutron.get_client(context=task.context)
     for port_like_obj in task.ports + task.portgroups:
         vif_port_id = (port_like_obj.internal_info.get(
             common.TENANT_VIF_KEY)
                        or port_like_obj.extra.get('vif_port_id'))
         if not vif_port_id:
             continue
         body = {
             'port': {
                 'binding:host_id': task.node.uuid,
                 'binding:vnic_type': neutron.VNIC_BAREMETAL,
                 'mac_address': port_like_obj.address
             }
         }
         try:
             client.update_port(vif_port_id, body)
         except neutron_exceptions.NeutronClientException as e:
             msg = (_('Unable to set binding:host_id for '
                      'neutron port %(port_id)s. Error: '
                      '%(err)s') % {
                          'port_id': vif_port_id,
                          'err': e
                      })
             LOG.exception(msg)
             raise exception.NetworkError(msg)
Exemple #10
0
    def add_provisioning_network(self, task):
        """Add the provisioning network to a node.

        :param task: A TaskManager instance.
        :raises: NetworkError when failed to set binding:host_id
        """
        LOG.debug("Binding flat network ports")
        node = task.node
        host_id = node.instance_info.get('nova_host_id')
        if not host_id:
            return

        client = neutron.get_client()
        for port_like_obj in task.ports + task.portgroups:
            vif_port_id = (
                port_like_obj.internal_info.get(common.TENANT_VIF_KEY) or
                port_like_obj.extra.get('vif_port_id')
            )
            if not vif_port_id:
                continue
            body = {
                'port': {
                    'binding:host_id': host_id
                }
            }
            try:
                client.update_port(vif_port_id, body)
            except neutron_exceptions.NeutronClientException as e:
                msg = (_('Unable to set binding:host_id for '
                         'neutron port %(port_id)s. Error: '
                         '%(err)s') % {'port_id': vif_port_id, 'err': e})
                LOG.exception(msg)
                raise exception.NetworkError(msg)
Exemple #11
0
def wait_for_port_status(client, port_id, status):
    """Wait for port status to be the desired status

    :param client: A Neutron client object.
    :param port_id: Neutron port_id
    :param status: Port's target status, can be ACTIVE, DOWN ... etc.
    :returns: boolean indicates that the port status matches the
        required value passed by param status.
    :raises: InvalidParameterValue if the port does not exist.
    :raises: exception.NetworkError if port status didn't match
        the required status after max retry attempts.
    """
    LOG.debug('Validating Port %(port_id)s status is %(status)s', {
        'port_id': port_id,
        'status': status
    })
    port_info = _get_port_by_uuid(client, port_id)
    LOG.debug('Port %(port_id)s status is: %(status)s', {
        'port_id': port_id,
        'status': port_info['status']
    })
    if port_info['status'] == status:
        return True
    raise exception.NetworkError(
        'Port %(port_id)s failed to reach status %(status)s' % {
            'port_id': port_id,
            'status': status
        })
Exemple #12
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
        """
        vif_id = vif_info['id']
        # Sort ports by pxe_enabled to ensure we always bind pxe_enabled ports
        # first
        sorted_ports = sorted(task.ports, key=lambda p: p.pxe_enabled,
                              reverse=True)
        free_ports = []
        # Check all ports to ensure this VIF isn't already attached
        for port in sorted_ports:
            port_id = port.internal_info.get(TENANT_VIF_KEY,
                                             port.extra.get('vif_port_id'))
            if port_id is None:
                free_ports.append(port)
            elif port_id == vif_id:
                raise exception.VifAlreadyAttached(
                    vif=vif_id, port_uuid=port.uuid)

        if not free_ports:
            raise exception.NoFreePhysicalPorts(vif=vif_id)

        # Get first free port
        port = free_ports.pop(0)

        # Check if the requested vif_id is a neutron port. If it is
        # then attempt to update the port's MAC address.
        try:
            client = neutron.get_client(task.context.auth_token)
            client.show_port(vif_id)
        except neutron_exceptions.NeutronClientException:
            # NOTE(sambetts): If a client error occurs this is because either
            # neutron doesn't exist because we're running in standalone
            # environment or we can't find a matching neutron port which means
            # a user might be requesting a non-neutron port. So skip trying to
            # update the neutron port MAC address in these cases.
            pass
        else:
            try:
                neutron.update_port_address(vif_id, port.address)
            except exception.FailedToUpdateMacOnPort:
                raise exception.NetworkError(_(
                    "Unable to attach VIF %(vif)s because Ironic can not "
                    "update Neutron port %(port)s MAC address to match "
                    "physical MAC address %(mac)s") % {
                        'vif': vif_id, 'port': vif_id, 'mac': port.address})

        int_info = port.internal_info
        int_info[TENANT_VIF_KEY] = vif_id
        port.internal_info = int_info
        port.save()
Exemple #13
0
def _verify_security_groups(security_groups, client):
    if not security_groups:
        return
    try:
        neutron_sec_groups = (
            client.list_security_groups().get('security_groups') or [])
    except neutron_exceptions.NeutronClientException as e:
        msg = (_("Could not retrieve neutron security groups %(exc)s") %
               {'exc': e})
        LOG.exception(msg)
        raise exception.NetworkError(msg)

    existing_sec_groups = [sec_group['id'] for sec_group in neutron_sec_groups]
    missing_sec_groups = set(security_groups) - set(existing_sec_groups)
    if missing_sec_groups:
        msg = (_('Security Groups specified in Ironic config '
                 '%(ir-sg)s are not found') %
               {'ir-sg': list(missing_sec_groups)})
        LOG.exception(msg)
        raise exception.NetworkError(msg)
Exemple #14
0
    def vif_attach(self, task, vif_info):
        """Attach a virtual network interface to a node

        Attach a virtual interface to a node. It will use the first free port
        group. If there are no free port groups, then the first available port
        (pxe_enabled preferably) is used.

        :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
        """
        vif_id = vif_info['id']

        port_like_obj = get_free_port_like_object(task, vif_id)

        client = neutron.get_client()
        # Address is optional for portgroups
        if port_like_obj.address:
            # Check if the requested vif_id is a neutron port. If it is
            # then attempt to update the port's MAC address.
            try:
                client.show_port(vif_id)
            except neutron_exceptions.NeutronClientException:
                # NOTE(sambetts): If a client error occurs this is because
                # either neutron doesn't exist because we're running in
                # standalone environment or we can't find a matching neutron
                # port which means a user might be requesting a non-neutron
                # port. So skip trying to update the neutron port MAC address
                # in these cases.
                pass
            else:
                try:
                    neutron.update_port_address(vif_id, port_like_obj.address)
                except exception.FailedToUpdateMacOnPort:
                    raise exception.NetworkError(
                        _("Unable to attach VIF %(vif)s because Ironic can not "
                          "update Neutron port %(port)s MAC address to match "
                          "physical MAC address %(mac)s") % {
                              'vif': vif_id,
                              'port': vif_id,
                              'mac': port_like_obj.address
                          })

        int_info = port_like_obj.internal_info
        int_info[TENANT_VIF_KEY] = vif_id
        port_like_obj.internal_info = int_info
        port_like_obj.save()
        # NOTE(vsaienko) allow to attach VIF to active instance.
        if task.node.provision_state == states.ACTIVE:
            plug_port_to_tenant_network(task, port_like_obj, client=client)
Exemple #15
0
    def configure_tenant_networks(self, task):
        """Configure tenant networks for a node.

        :param task: A TaskManager instance.
        :raises: NetworkError
        """
        node = task.node
        ports = task.ports
        LOG.info(_LI('Mapping instance ports to %s'), node.uuid)

        # TODO(russell_h): this is based on the broken assumption that the
        # number of Neutron ports will match the number of physical ports.
        # Instead, we should probably list ports for this instance in
        # Neutron and update all of those with the appropriate portmap.
        if not ports:
            msg = _("No ports are associated with node %s") % node.uuid
            LOG.error(msg)
            raise exception.NetworkError(msg)
        ports = [p for p in ports if not p.portgroup_id]
        portgroups = task.portgroups

        client = neutron.get_client()
        pobj_without_vif = 0
        for port_like_obj in ports + portgroups:

            try:
                common.plug_port_to_tenant_network(task,
                                                   port_like_obj,
                                                   client=client)
            except exception.VifNotAttached:
                pobj_without_vif += 1
                continue

        if pobj_without_vif == len(ports + portgroups):
            msg = _("No neutron ports or portgroups are associated with "
                    "node %s") % node.uuid
            LOG.error(msg)
            raise exception.NetworkError(msg)
Exemple #16
0
 def test_update_port_address_unbind_port_failed(self, mock_unp,
                                                 mock_client):
     address = 'fe:54:00:77:07:d9'
     port_id = 'fake-port-id'
     mock_client.return_value.show_port.return_value = {
         'port': {
             'binding:profile': 'foo',
             'binding:host_id': 'host'
         }
     }
     mock_unp.side_effect = (exception.NetworkError('boom'))
     self.assertRaises(exception.FailedToUpdateMacOnPort,
                       neutron.update_port_address, port_id, address)
     mock_unp.assert_called_once_with(port_id, client=mock_client())
     self.assertFalse(mock_client.return_value.update_port.called)
Exemple #17
0
def validate_network(uuid_or_name, net_type=_('network')):
    """Check that the given network is present.

    :param uuid_or_name: network UUID or name
    :param net_type: human-readable network type for error messages
    :return: network UUID
    :raises: MissingParameterValue if uuid_or_name is empty
    :raises: NetworkError on failure to contact Neutron
    :raises: InvalidParameterValue for missing or duplicated network
    """
    if not uuid_or_name:
        raise exception.MissingParameterValue(
            _('UUID or name of %s is not set in configuration') % net_type)

    if uuidutils.is_uuid_like(uuid_or_name):
        filters = {'id': uuid_or_name}
    else:
        filters = {'name': uuid_or_name}

    try:
        client = get_client()
        networks = client.list_networks(fields=['id'], **filters)
    except neutron_exceptions.NeutronClientException as exc:
        raise exception.NetworkError(
            _('Could not retrieve network list: %s') % exc)

    LOG.debug('Got list of networks matching %(cond)s: %(result)s', {
        'cond': filters,
        'result': networks
    })
    networks = [n['id'] for n in networks.get('networks', [])]
    if not networks:
        raise exception.InvalidParameterValue(
            _('%(type)s with name or UUID %(uuid_or_name)s was not found') % {
                'type': net_type,
                'uuid_or_name': uuid_or_name
            })
    elif len(networks) > 1:
        raise exception.InvalidParameterValue(
            _('More than one %(type)s was found for name %(name)s: %(nets)s') %
            {
                'name': uuid_or_name,
                'nets': ', '.join(networks),
                'type': net_type
            })

    return networks[0]
Exemple #18
0
def _get_network_by_uuid_or_name(client,
                                 uuid_or_name,
                                 net_type=_('network'),
                                 **params):
    """Return a neutron network by UUID or name.

    :param client: A Neutron client object.
    :param uuid_or_name: network UUID or name
    :param net_type: human-readable network type for error messages
    :param params: Additional parameters to pass to the neutron client
        list_networks method.
    :returns: A dict describing the neutron network.
    :raises: NetworkError on failure to contact Neutron
    :raises: InvalidParameterValue for missing or duplicated network
    """
    if uuidutils.is_uuid_like(uuid_or_name):
        params['id'] = uuid_or_name
    else:
        params['name'] = uuid_or_name

    try:
        networks = client.list_networks(**params)
    except neutron_exceptions.NeutronClientException as exc:
        raise exception.NetworkError(
            _('Could not retrieve network list: %s') % exc)

    LOG.debug('Got list of networks matching %(cond)s: %(result)s', {
        'cond': params,
        'result': networks
    })
    networks = networks.get('networks', [])
    if not networks:
        raise exception.InvalidParameterValue(
            _('%(type)s with name or UUID %(uuid_or_name)s was not found') % {
                'type': net_type,
                'uuid_or_name': uuid_or_name
            })
    elif len(networks) > 1:
        network_ids = [n['id'] for n in networks]
        raise exception.InvalidParameterValue(
            _('More than one %(type)s was found for name %(name)s: %(nets)s') %
            {
                'name': uuid_or_name,
                'nets': ', '.join(network_ids),
                'type': net_type
            })
    return networks[0]
Exemple #19
0
    def _get_fixed_ip_address(self, port_id, client):
        """Get a Neutron port's fixed ip address.

        :param port_id: Neutron port id.
        :param client: Neutron client instance.
        :returns: Neutron port ip address.
        :raises: NetworkError
        :raises: InvalidIPv4Address
        :raises: FailedToGetIPAddressOnPort
        """
        ip_address = None
        try:
            neutron_port = client.get_port(port_id)
        except openstack_exc.OpenStackCloudException:
            raise exception.NetworkError(
                _('Could not retrieve neutron port: %s') % port_id)

        fixed_ips = neutron_port.get('fixed_ips')

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

        if ip_address:
            try:
                if (ipaddress.ip_address(ip_address).version == 4
                        or ipaddress.ip_address(ip_address).version == 6):
                    return ip_address
                else:
                    LOG.error(
                        "Neutron returned invalid IP "
                        "address %(ip_address)s on port %(port_id)s.", {
                            'ip_address': ip_address,
                            'port_id': port_id
                        })

                    raise exception.InvalidIPv4Address(ip_address=ip_address)
            except ValueError as exc:
                LOG.error(
                    "An Invalid IP address was supplied and failed "
                    "basic validation: %s", exc)
                raise exception.InvalidIPAddress(ip_address=ip_address)
        else:
            LOG.error("No IP address assigned to Neutron port %s.", port_id)
            raise exception.FailedToGetIPAddressOnPort(port_id=port_id)
Exemple #20
0
def _validate_agent(client, **kwargs):
    """Check that the given neutron agent is alive

    :param client: Neutron client
    :param kwargs: Additional parameters to pass to the neutron client
            list_agents method.
    :returns: A boolean to describe the agent status, if more than one agent
        returns by the client then return True if at least one of them is
        alive.
    :raises: NetworkError in case of failure contacting Neutron.
    """
    try:
        agents = client.list_agents(**kwargs)['agents']
        for agent in agents:
            if agent['alive']:
                return True
            return False
    except neutron_exceptions.NeutronClientException:
        raise exception.NetworkError('Failed to contact Neutron server')
Exemple #21
0
def _get_port_by_uuid(client, port_uuid):
    """Return a neutron port by UUID.

    :param client: A Neutron client object.
    :param port_uuid: UUID of a Neutron port to query.
    :returns: A dict describing the neutron port.
    :raises: InvalidParameterValue if the port does not exist.
    :raises: NetworkError on failure to contact Neutron.
    """
    try:
        port = client.get_port(port_uuid)
    except openstack_exc.ResourceNotFound:
        raise exception.InvalidParameterValue(
            _('Neutron port %(port_uuid)s was not found') %
            {'port_uuid': port_uuid})
    except openstack_exc.OpenStackCloudException as exc:
        raise exception.NetworkError(
            _('Could not retrieve neutron port: %s') % exc)

    return port
Exemple #22
0
def _get_port_by_uuid(client, port_uuid, **params):
    """Return a neutron port by UUID.

    :param client: A Neutron client object.
    :param port_uuid: UUID of a Neutron port to query.
    :param params: Additional parameters to pass to the neutron client
        show_port method.
    :returns: A dict describing the neutron port.
    :raises: InvalidParameterValue if the port does not exist.
    :raises: NetworkError on failure to contact Neutron.
    """
    try:
        port = client.show_port(port_uuid, **params)
    except neutron_exceptions.PortNotFoundClient:
        raise exception.InvalidParameterValue(
            _('Neutron port %(port_uuid)s was not found') %
            {'port_uuid': port_uuid})
    except neutron_exceptions.NeutronClientException as exc:
        raise exception.NetworkError(_('Could not retrieve neutron port: %s') %
                                     exc)
    return port['port']
Exemple #23
0
 def _bind_flat_ports(self, task):
     LOG.debug("Binding flat network ports")
     for port_like_obj in task.ports + task.portgroups:
         vif_port_id = (
             port_like_obj.internal_info.get(common.TENANT_VIF_KEY)
             or port_like_obj.extra.get('vif_port_id')
         )
         if not vif_port_id:
             continue
         port_attrs = {'binding:host_id': task.node.uuid,
                       'binding:vnic_type': neutron.VNIC_BAREMETAL,
                       'mac_address': port_like_obj.address}
         try:
             neutron.update_neutron_port(task.context,
                                         vif_port_id, port_attrs)
         except openstack_exc.OpenStackCloudException as e:
             msg = (_('Unable to set binding:host_id for '
                      'neutron port %(port_id)s. Error: '
                      '%(err)s') % {'port_id': vif_port_id, 'err': e})
             LOG.exception(msg)
             raise exception.NetworkError(msg)
Exemple #24
0
    def _get_fixed_ip_address(self, port_uuid, client):
        """Get a Neutron port's fixed ip address.

        :param port_uuid: Neutron port id.
        :param client: Neutron client instance.
        :returns: Neutron port ip address.
        :raises: NetworkError
        :raises: InvalidIPv4Address
        :raises: FailedToGetIPAddressOnPort
        """
        ip_address = None
        try:
            neutron_port = client.show_port(port_uuid).get('port')
        except neutron_client_exc.NeutronClientException:
            raise exception.NetworkError(
                _('Could not retrieve neutron port: %s') % port_uuid)

        fixed_ips = neutron_port.get('fixed_ips')

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

        if ip_address:
            if netutils.is_valid_ipv4(ip_address):
                return ip_address
            else:
                LOG.error(
                    "Neutron returned invalid IPv4 "
                    "address %(ip_address)s on port %(port_uuid)s.", {
                        'ip_address': ip_address,
                        'port_uuid': port_uuid
                    })
                raise exception.InvalidIPv4Address(ip_address=ip_address)
        else:
            LOG.error("No IP address assigned to Neutron port %s.", port_uuid)
            raise exception.FailedToGetIPAddressOnPort(port_id=port_uuid)
Exemple #25
0
    def add_provisioning_network(self, task):
        """Add the provisioning network to a node.

        :param task: A TaskManager instance.
        :raises: NetworkError when failed to set binding:host_id
        """
        LOG.debug("Binding flat network ports")
        node = task.node
        host_id = node.instance_info.get('nova_host_id')
        if not host_id:
            return

        # FIXME(sambetts): Uncomment when we support vifs attached to
        # portgroups
        #
        # ports = [p for p in task.ports if not p.portgroup_id]
        # portgroups = task.portgroups

        client = neutron.get_client(task.context.auth_token)
        for port_like_obj in task.ports:  # + portgroups:
            vif_port_id = (
                port_like_obj.extra.get('vif_port_id')
                or port_like_obj.internal_info.get('tenant_vif_port_id'))
            if not vif_port_id:
                continue
            body = {'port': {'binding:host_id': host_id}}
            try:
                client.update_port(vif_port_id, body)
            except neutron_exceptions.NeutronClientException as e:
                msg = (_('Unable to set binding:host_id for '
                         'neutron port %(port_id)s. Error: '
                         '%(err)s') % {
                             'port_id': vif_port_id,
                             'err': e
                         })
                LOG.exception(msg)
                raise exception.NetworkError(msg)
Exemple #26
0
def plug_port_to_tenant_network(task, port_like_obj, client=None):
    """Plug port like object to tenant network.

    :param task: A TaskManager instance.
    :param port_like_obj: port-like object to plug.
    :param client: Neutron client instance.
    :raises NetworkError: if failed to update Neutron port.
    :raises VifNotAttached if tenant VIF is not associated with port_like_obj.
    """

    node = task.node
    local_link_info = []
    client_id_opt = None

    vif_id = (port_like_obj.internal_info.get(TENANT_VIF_KEY)
              or port_like_obj.extra.get('vif_port_id'))

    if not vif_id:
        obj_name = port_like_obj.__class__.__name__.lower()
        raise exception.VifNotAttached(
            _("Tenant VIF is not associated with %(obj_name)s "
              "%(obj_id)s") % {
                  'obj_name': obj_name,
                  'obj_id': port_like_obj.uuid
              })

    LOG.debug('Mapping tenant port %(vif_id)s to node '
              '%(node_id)s', {
                  'vif_id': vif_id,
                  'node_id': node.uuid
              })

    if isinstance(port_like_obj, objects.Portgroup):
        pg_ports = [
            p for p in task.ports if p.portgroup_id == port_like_obj.id
        ]
        for port in pg_ports:
            local_link_info.append(port.local_link_connection)
    else:
        # We iterate only on ports or portgroups, no need to check
        # that it is a port
        local_link_info.append(port_like_obj.local_link_connection)
        client_id = port_like_obj.extra.get('client-id')
        if client_id:
            client_id_opt = ({'opt_name': 'client-id', 'opt_value': client_id})

    # NOTE(sambetts) Only update required binding: attributes,
    # because other port attributes may have been set by the user or
    # nova.
    body = {
        'port': {
            'binding:vnic_type': 'baremetal',
            'binding:host_id': node.uuid,
            'binding:profile': {
                'local_link_information': local_link_info,
            },
        }
    }
    if client_id_opt:
        body['port']['extra_dhcp_opts'] = [client_id_opt]

    if not client:
        client = neutron.get_client()

    try:
        client.update_port(vif_id, body)
    except neutron_exceptions.ConnectionFailed as e:
        msg = (_('Could not add public network VIF %(vif)s '
                 'to node %(node)s, possible network issue. %(exc)s') % {
                     'vif': vif_id,
                     'node': node.uuid,
                     'exc': e
                 })
        LOG.error(msg)
        raise exception.NetworkError(msg)
Exemple #27
0
    def vif_attach(self, task, vif_info):
        """Attach a virtual network interface to a node

        Attach a virtual interface to a node.  When selecting a port or
        portgroup to attach the virtual interface to, the following ordered
        criteria are applied:

        * Require ports or portgroups to have a physical network that is either
          None or one of the VIF's allowed physical networks.
        * Prefer ports or portgroups with a physical network field which is not
          None.
        * Prefer portgroups to ports.
        * Prefer ports with PXE enabled.

        :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
        :raises: PortgroupPhysnetInconsistent if one of the node's portgroups
                 has ports which are not all assigned the same physical
                 network.
        """
        vif_id = vif_info['id']
        client = neutron.get_client(context=task.context)

        # Determine whether any of the node's ports have a physical network. If
        # not, we don't need to check the VIF's network's physical networks as
        # they will not affect the VIF to port mapping.
        physnets = set()
        if any(port.physical_network is not None for port in task.ports):
            physnets = neutron.get_physnets_by_port_uuid(client, vif_id)

            if len(physnets) > 1:
                # NOTE(mgoddard): Neutron cannot currently handle hosts which
                # are mapped to multiple segments in the same routed network.
                node_physnets = network.get_physnets_for_node(task)
                if len(node_physnets.intersection(physnets)) > 1:
                    reason = _("Node has ports which map to multiple segments "
                               "of the routed network to which the VIF is "
                               "attached. Currently neutron only supports "
                               "hosts which map to one segment of a routed "
                               "network")
                    raise exception.VifInvalidForAttach(node=task.node.uuid,
                                                        vif=vif_id,
                                                        reason=reason)

        port_like_obj = get_free_port_like_object(task, vif_id, physnets)

        # Address is optional for portgroups
        if port_like_obj.address:
            try:
                neutron.update_port_address(vif_id,
                                            port_like_obj.address,
                                            context=task.context)
            except exception.FailedToUpdateMacOnPort:
                raise exception.NetworkError(
                    _("Unable to attach VIF %(vif)s because Ironic can not "
                      "update Neutron port %(port)s MAC address to match "
                      "physical MAC address %(mac)s") % {
                          'vif': vif_id,
                          'port': vif_id,
                          'mac': port_like_obj.address
                      })

        self._save_vif_to_port_like_obj(port_like_obj, vif_id)

        # NOTE(vsaienko) allow to attach VIF to active instance.
        if task.node.provision_state == states.ACTIVE:
            plug_port_to_tenant_network(task, port_like_obj, client=client)
Exemple #28
0
def plug_port_to_tenant_network(task, port_like_obj, client=None):
    """Plug port like object to tenant network.

    :param task: A TaskManager instance.
    :param port_like_obj: port-like object to plug.
    :param client: Neutron client instance.
    :raises: NetworkError if failed to update Neutron port.
    :raises: VifNotAttached if tenant VIF is not associated with port_like_obj.
    """

    node = task.node
    local_link_info = []
    local_group_info = None
    client_id_opt = None

    vif_id = (port_like_obj.internal_info.get(TENANT_VIF_KEY)
              or port_like_obj.extra.get('vif_port_id'))

    if not vif_id:
        obj_name = port_like_obj.__class__.__name__.lower()
        raise exception.VifNotAttached(
            _("Tenant VIF is not associated with %(obj_name)s "
              "%(obj_id)s") % {
                  'obj_name': obj_name,
                  'obj_id': port_like_obj.uuid
              })

    LOG.debug('Mapping tenant port %(vif_id)s to node '
              '%(node_id)s', {
                  'vif_id': vif_id,
                  'node_id': node.uuid
              })

    if isinstance(port_like_obj, objects.Portgroup):
        pg_ports = [
            p for p in task.ports if p.portgroup_id == port_like_obj.id
        ]
        for port in pg_ports:
            local_link_info.append(port.local_link_connection)
        local_group_info = neutron.get_local_group_information(
            task, port_like_obj)
    else:
        # We iterate only on ports or portgroups, no need to check
        # that it is a port
        local_link_info.append(port_like_obj.local_link_connection)
        client_id = port_like_obj.extra.get('client-id')
        if client_id:
            client_id_opt = ({
                'opt_name': DHCP_CLIENT_ID,
                'opt_value': client_id
            })

    # NOTE(sambetts) Only update required binding: attributes,
    # because other port attributes may have been set by the user or
    # nova.
    port_attrs = {
        'binding:vnic_type': neutron.VNIC_BAREMETAL,
        'binding:host_id': node.uuid
    }
    # NOTE(kaifeng) Only update mac address when it's available
    if port_like_obj.address:
        port_attrs['mac_address'] = port_like_obj.address
    binding_profile = {'local_link_information': local_link_info}
    if local_group_info:
        binding_profile['local_group_information'] = local_group_info
    port_attrs['binding:profile'] = binding_profile

    if client_id_opt:
        port_attrs['extra_dhcp_opts'] = [client_id_opt]

    is_smart_nic = neutron.is_smartnic_port(port_like_obj)
    if is_smart_nic:
        link_info = local_link_info[0]
        LOG.debug(
            'Setting hostname as host_id in case of Smart NIC, '
            'port %(port_id)s, hostname %(hostname)s', {
                'port_id': vif_id,
                'hostname': link_info['hostname']
            })
        port_attrs['binding:host_id'] = link_info['hostname']
        port_attrs['binding:vnic_type'] = neutron.VNIC_SMARTNIC

    if not client:
        client = neutron.get_client(context=task.context)

    if is_smart_nic:
        neutron.wait_for_host_agent(client, port_attrs['binding:host_id'])

    try:
        neutron.update_neutron_port(task.context, vif_id, port_attrs)
        if is_smart_nic:
            neutron.wait_for_port_status(client, vif_id, 'ACTIVE')
    except openstack_exc.OpenStackCloudException as e:
        msg = (_('Could not add public network VIF %(vif)s '
                 'to node %(node)s, possible network issue. %(exc)s') % {
                     'vif': vif_id,
                     'node': node.uuid,
                     'exc': e
                 })
        LOG.error(msg)
        raise exception.NetworkError(msg)
Exemple #29
0
    def configure_tenant_networks(self, task):
        """Configure tenant networks for a node.

        :param task: A TaskManager instance.
        :raises: NetworkError
        """
        node = task.node
        ports = task.ports
        LOG.info(_LI('Mapping instance ports to %s'), node.uuid)

        # TODO(russell_h): this is based on the broken assumption that the
        # number of Neutron ports will match the number of physical ports.
        # Instead, we should probably list ports for this instance in
        # Neutron and update all of those with the appropriate portmap.
        if not ports:
            msg = _("No ports are associated with node %s") % node.uuid
            LOG.error(msg)
            raise exception.NetworkError(msg)
        ports = [p for p in ports if not p.portgroup_id]
        portgroups = task.portgroups

        portmap = neutron.get_node_portmap(task)

        client = neutron.get_client(task.context.auth_token)
        pobj_without_vif = 0
        for port_like_obj in ports + portgroups:
            vif_port_id = (
                port_like_obj.internal_info.get('tenant_vif_port_id')
                or port_like_obj.extra.get('vif_port_id'))

            if not vif_port_id:
                pobj_without_vif += 1
                continue

            LOG.debug(
                'Mapping tenant port %(vif_port_id)s to node '
                '%(node_id)s', {
                    'vif_port_id': vif_port_id,
                    'node_id': node.uuid
                })
            local_link_info = []
            client_id_opt = None
            if isinstance(port_like_obj, objects.Portgroup):
                pg_ports = [
                    p for p in task.ports if p.portgroup_id == port_like_obj.id
                ]
                for port in pg_ports:
                    local_link_info.append(portmap[port.uuid])
            else:
                # We iterate only on ports or portgroups, no need to check
                # that it is a port
                local_link_info.append(portmap[port_like_obj.uuid])
                client_id = port_like_obj.extra.get('client-id')
                if client_id:
                    client_id_opt = ({
                        'opt_name': 'client-id',
                        'opt_value': client_id
                    })
            body = {
                'port': {
                    'device_owner': 'baremetal:none',
                    'device_id': node.instance_uuid or node.uuid,
                    'admin_state_up': True,
                    'binding:vnic_type': 'baremetal',
                    'binding:host_id': node.uuid,
                    'binding:profile': {
                        'local_link_information': local_link_info,
                    },
                }
            }
            if client_id_opt:
                body['port']['extra_dhcp_opts'] = [client_id_opt]

            try:
                client.update_port(vif_port_id, body)
            except neutron_exceptions.ConnectionFailed as e:
                msg = (_('Could not add public network VIF %(vif)s '
                         'to node %(node)s, possible network issue. %(exc)s') %
                       {
                           'vif': vif_port_id,
                           'node': node.uuid,
                           'exc': e
                       })
                LOG.error(msg)
                raise exception.NetworkError(msg)

        if pobj_without_vif == len(ports + portgroups):
            msg = _("No neutron ports or portgroups are associated with "
                    "node %s") % node.uuid
            LOG.error(msg)
            raise exception.NetworkError(msg)
Exemple #30
0
def add_ports_to_network(task, network_uuid, security_groups=None):
    """Create neutron ports to boot the ramdisk.

    Create neutron ports for each pxe_enabled port on task.node to boot
    the ramdisk.

    If the config option 'neutron.add_all_ports' is set, neutron ports
    for non-pxe-enabled ports are also created -- these neutron ports
    will not have any assigned IP addresses.

    :param task: a TaskManager instance.
    :param network_uuid: UUID of a neutron network where ports will be
        created.
    :param security_groups: List of Security Groups UUIDs to be used for
        network.
    :raises: NetworkError
    :returns: a dictionary in the form {port.uuid: neutron_port['id']}
    """
    client = get_client(context=task.context)
    node = task.node
    add_all_ports = CONF.neutron.add_all_ports

    # If Security Groups are specified, verify that they exist
    _verify_security_groups(security_groups, client)

    LOG.debug('For node %(node)s, creating neutron ports on network '
              '%(network_uuid)s using %(net_iface)s network interface.',
              {'net_iface': task.driver.network.__class__.__name__,
               'node': node.uuid, 'network_uuid': network_uuid})

    body = {
        'port': {
            'network_id': network_uuid,
            'admin_state_up': True,
            'binding:vnic_type': VNIC_BAREMETAL,
        }
    }
    # separate out fields that can only be updated by admins
    update_body = {
        'port': {
            'binding:host_id': node.uuid,
            'device_owner': 'baremetal:none',
        }
    }
    if security_groups:
        body['port']['security_groups'] = security_groups

    # Since instance_uuid will not be available during cleaning
    # operations, we need to check that and populate them only when
    # available
    body['port']['device_id'] = node.instance_uuid or node.uuid

    ports = {}
    failures = []
    portmap = get_node_portmap(task)

    if not add_all_ports:
        ports_to_create = [p for p in task.ports if p.pxe_enabled]
    else:
        ports_to_create = task.ports
    if not ports_to_create:
        pxe_enabled = 'PXE-enabled ' if not add_all_ports else ''
        raise exception.NetworkError(_(
            "No available %(enabled)sports on node %(node)s.") %
            {'enabled': pxe_enabled, 'node': node.uuid})

    for ironic_port in ports_to_create:
        # Start with a clean state for each port
        port_body = copy.deepcopy(body)
        update_port_body = copy.deepcopy(update_body)
        # Skip ports that are missing required information for deploy.
        if not validate_port_info(node, ironic_port):
            failures.append(ironic_port.uuid)
            continue
        update_port_body['port']['mac_address'] = ironic_port.address
        binding_profile = {'local_link_information':
                           [portmap[ironic_port.uuid]]}
        update_port_body['port']['binding:profile'] = binding_profile

        if not ironic_port.pxe_enabled:
            LOG.debug("Adding port %(port)s to network %(net)s for "
                      "provisioning without an IP allocation.",
                      {'port': ironic_port.uuid,
                       'net': network_uuid})
            port_body['fixed_ips'] = []

        is_smart_nic = is_smartnic_port(ironic_port)
        if is_smart_nic:
            link_info = binding_profile['local_link_information'][0]
            LOG.debug('Setting hostname as host_id in case of Smart NIC, '
                      'port %(port_id)s, hostname %(hostname)s',
                      {'port_id': ironic_port.uuid,
                       'hostname': link_info['hostname']})
            update_port_body['port']['binding:host_id'] = link_info['hostname']

            # TODO(hamdyk): use portbindings.VNIC_SMARTNIC from neutron-lib
            port_body['port']['binding:vnic_type'] = VNIC_SMARTNIC
        client_id = ironic_port.extra.get('client-id')
        if client_id:
            client_id_opt = {'opt_name': DHCP_CLIENT_ID,
                             'opt_value': client_id}
            extra_dhcp_opts = port_body['port'].get('extra_dhcp_opts', [])
            extra_dhcp_opts.append(client_id_opt)
            port_body['port']['extra_dhcp_opts'] = extra_dhcp_opts
        try:
            if is_smart_nic:
                wait_for_host_agent(
                    client, update_port_body['port']['binding:host_id'])
            port = client.create_port(port_body)
            update_neutron_port(task.context, port['port']['id'],
                                update_port_body)
            if CONF.neutron.dhcpv6_stateful_address_count > 1:
                _add_ip_addresses_for_ipv6_stateful(task.context, port, client)
            if is_smart_nic:
                wait_for_port_status(client, port['port']['id'], 'ACTIVE')
        except neutron_exceptions.NeutronClientException as e:
            failures.append(ironic_port.uuid)
            LOG.warning("Could not create neutron port for node's "
                        "%(node)s port %(ir-port)s on the neutron "
                        "network %(net)s. %(exc)s",
                        {'net': network_uuid, 'node': node.uuid,
                         'ir-port': ironic_port.uuid, 'exc': e})
        else:
            ports[ironic_port.uuid] = port['port']['id']

    if failures:
        if len(failures) == len(ports_to_create):
            rollback_ports(task, network_uuid)
            raise exception.NetworkError(_(
                "Failed to create neutron ports for node's %(node)s ports "
                "%(ports)s.") % {'node': node.uuid, 'ports': ports_to_create})
        else:
            LOG.warning("Some errors were encountered when updating "
                        "vif_port_id for node %(node)s on "
                        "the following ports: %(ports)s.",
                        {'node': node.uuid, 'ports': failures})
    else:
        LOG.info('For node %(node_uuid)s in network %(net)s, successfully '
                 'created ports (ironic ID: neutron ID): %(ports)s.',
                 {'node_uuid': node.uuid, 'net': network_uuid, 'ports': ports})

    return ports