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()
def _vif_attached(port_like_obj, vif_id): """Check if VIF is already attached to a port or portgroup. Raises an exception if a VIF with id=vif_id is attached to the port-like (Port or Portgroup) object. Otherwise, returns whether a VIF is attached. :param port_like_obj: port-like object to check. :param vif_id: identifier of the VIF to look for in port_like_obj. :returns: True if a VIF (but not vif_id) is attached to port_like_obj, False otherwise. :raises: VifAlreadyAttached, if vif_id is attached to port_like_obj. """ attached_vif_id = port_like_obj.internal_info.get(TENANT_VIF_KEY) if attached_vif_id == vif_id: raise exception.VifAlreadyAttached( object_type=port_like_obj.__class__.__name__, vif=vif_id, object_uuid=port_like_obj.uuid) return attached_vif_id is not None