Ejemplo n.º 1
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)
Ejemplo n.º 2
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()

        # 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):
            try:
                physnets = neutron.get_physnets_by_port_uuid(client, vif_id)
            except (exception.InvalidParameterValue, exception.NetworkError):
                # TODO(mgoddard): Remove this except clause and handle errors
                # properly. We can do this once a strategy has been determined
                # for handling the tempest VIF tests in an environment that
                # may not support neutron.
                # 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. Assume no physical network information exists in these
                # cases.
                pass

            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:
            # 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:
                # TODO(mgoddard): Remove this except clause and handle errors
                # properly. We can do this once a strategy has been determined
                # for handling the tempest VIF tests in an environment that
                # may not support neutron.
                # 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)