def vif_detach(self, task, vif_id): """Detach a virtual network interface from a node :param task: A TaskManager instance. :param vif_id: A VIF ID to detach :raises: VifNotAttached if VIF not attached. :raises: NetworkError: if unbind Neutron port failed. """ # NOTE(vsaienko) We picking object to attach on vif-attach side. # Here we should only detach VIF and shouldn't duplicate/follow # attach rules, just walk over all objects and detach VIF. for port_like_obj in task.portgroups + task.ports: # FIXME(sambetts) Remove this when we no longer support a nova # driver that uses port.extra vif_port_id = port_like_obj.internal_info.get( TENANT_VIF_KEY, port_like_obj.extra.get("vif_port_id")) if vif_port_id == vif_id: int_info = port_like_obj.internal_info extra = port_like_obj.extra int_info.pop(TENANT_VIF_KEY, None) extra.pop('vif_port_id', None) port_like_obj.extra = extra port_like_obj.internal_info = int_info port_like_obj.save() # NOTE(vsaienko) allow to unplug VIFs from ACTIVE instance. if task.node.provision_state == states.ACTIVE: neutron.unbind_neutron_port(vif_port_id) break else: raise exception.VifNotAttached(vif=vif_id, node=task.node.uuid)
def _get_port_like_obj_by_vif_id(self, task, vif_id): """Lookup a port or portgroup by its attached VIF ID. :param task: A TaskManager instance. :param vif_id: ID of the attached VIF. :returns: A Port or Portgroup object to which the VIF is attached. :raises: VifNotAttached if the VIF is not attached. """ for port_like_obj in task.portgroups + task.ports: vif_port_id = self._get_vif_id_by_port_like_obj(port_like_obj) if vif_port_id == vif_id: return port_like_obj raise exception.VifNotAttached(vif=vif_id, node=task.node.uuid)
def vif_detach(self, task, vif_id): """Detach a virtual network interface from a node :param task: A TaskManager instance. :param vif_id: A VIF ID to detach :raises: VifNotAttached """ for port in task.ports: # FIXME(sambetts) Remove this when we no longer support a nova # driver that uses port.extra if (port.extra.get("vif_port_id") == vif_id or port.internal_info.get(TENANT_VIF_KEY) == vif_id): int_info = port.internal_info extra = port.extra int_info.pop(TENANT_VIF_KEY, None) extra.pop('vif_port_id', None) port.extra = extra port.internal_info = int_info port.save() break else: raise exception.VifNotAttached(vif=vif_id, node=task.node.uuid)
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)
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)