def delete_network_postcommit(self, context): """Send network delete request to Arista HW.""" network = context.current segments = context.network_segments if not self.rpc.hpb_supported(): # Hierarchical port binding is not supported by CVX, only # send the request if network type is VLAN. if segments[0][driver_api.NETWORK_TYPE] != p_const.TYPE_VLAN: # If networtk type is not VLAN, do nothing return network_id = network['id'] tenant_id = network['tenant_id'] or INTERNAL_TENANT_ID with self.eos_sync_lock: # Succeed deleting network in case EOS is not accessible. # EOS state will be updated by sync thread once EOS gets # alive. try: self.rpc.delete_network(tenant_id, network_id, segments) # if necessary, delete tenant as well. self.delete_tenant(tenant_id) except arista_exc.AristaRpcError as err: LOG.error( _LE('delete_network_postcommit: Did not delete ' 'network %(network_id)s. Reason: %(err)s'), { 'network_id': network_id, 'err': err })
def get_tenants(self): path = 'region/' + self.region + '/tenant' tenants = self._send_api_request(path, 'GET') d = {} for ten in tenants: ten['tenantId'] = ten.pop('id') nets = self.get_networks(ten['tenantId']) netDict = {} try: for net in nets: net['networkId'] = net.pop('id') net['networkName'] = net.pop('name') netDict[net['networkId']] = net except Exception as exc: LOG.error(_LE('Failed to get tenant network %(net)s. ' 'Reason: %(exc)s'), {'net': net, 'exc': exc}) ten['tenantNetworks'] = netDict vms = self.get_vms_for_tenant(ten['tenantId']) vmDict = dict((v['id'], v) for v in vms) ten['tenantVmInstances'] = vmDict routers = self.get_routers_for_tenant(ten['tenantId']) routerDict = dict((r['id'], r) for r in routers) ten['tenantRouterInstances'] = routerDict bms = self.get_baremetals_for_tenant(ten['tenantId']) bmDict = dict((b['id'], b) for b in bms) ten['tenantBaremetalInstances'] = bmDict d[ten['tenantId']] = ten return d
def remove_router_interface(self, context, router_id, interface_info): """Remove a subnet of a network from an existing router.""" router_to_del = ( super(AristaL3ServicePlugin, self).remove_router_interface( context, router_id, interface_info) ) # Get network information of the subnet that is being removed core = directory.get_plugin() subnet = core.get_subnet(context, router_to_del['subnet_id']) network_id = subnet['network_id'] # For SVI removal from Arista HW, segmentation ID is needed ml2_db = NetworkContext(self, context, {'id': network_id}) seg_id = ml2_db.network_segments[0]['segmentation_id'] router = self.get_router(context, router_id) router_info = copy.deepcopy(router_to_del) router_info['seg_id'] = seg_id router_info['name'] = router['name'] try: self.driver.remove_router_interface(context, router_info) return router_to_del except Exception as exc: LOG.error(_LE("Error removing interface %(interface)s from " "router %(router_id)s on Arista HW" "Exception =(exc)s"), {'interface': interface_info, 'router_id': router_id, 'exc': exc})
def update_network_postcommit(self, context): """At the moment we only support network name change If network name is changed, a new network create request is sent to the Arista Hardware. """ new_network = context.current orig_network = context.original if ((new_network['name'] != orig_network['name']) or (new_network['shared'] != orig_network['shared'])): network_id = new_network['id'] network_name = new_network['name'] tenant_id = (new_network['tenant_id'] or constants.INTERNAL_TENANT_ID) shared_net = new_network['shared'] with self.eos_sync_lock: try: network_dict = { 'network_id': network_id, 'segments': context.network_segments, 'network_name': network_name, 'shared': shared_net } self.rpc.create_network(tenant_id, network_dict) except arista_exc.AristaRpcError as err: LOG.error( _LE('update_network_postcommit: Did not ' 'update network %(name)s. ' 'Reason: %(err)s'), { 'name': network_name, 'err': err })
def create_routers(self, routers): for r in routers: try: self.driver.create_router(self, r) except Exception: LOG.error(_LE("Error Adding router %(router_id)s " "on Arista HW"), {'router_id': r})
def remove_router_interface(self, context, router_id, interface_info): """Remove a subnet of a network from an existing router.""" new_router = (super(AristaL3ServicePlugin, self).remove_router_interface( context, router_id, interface_info)) # Get network information of the subnet that is being removed subnet = self.get_subnet(context, new_router['subnet_id']) network_id = subnet['network_id'] # For SVI removal from Arista HW, segmentation ID is needed ml2_db = NetworkContext(self, context, {'id': network_id}) seg_id = ml2_db.network_segments[0]['segmentation_id'] router = super(AristaL3ServicePlugin, self).get_router(context, router_id) router_info = copy.deepcopy(new_router) router_info['seg_id'] = seg_id router_info['name'] = router['name'] try: self.driver.remove_router_interface(context, router_info) return new_router except Exception as exc: LOG.error( _LE("Error removing interface %(interface)s from " "router %(router_id)s on Arista HW" "Exception =(exc)s"), { 'interface': interface_info, 'router_id': router_id, 'exc': exc })
def create_network_postcommit(self, context): """Provision the network on the Arista Hardware.""" network = context.current network_id = network['id'] network_name = network['name'] tenant_id = network['tenant_id'] or INTERNAL_TENANT_ID segments = context.network_segments shared_net = network['shared'] with self.eos_sync_lock: if db_lib.is_network_provisioned(tenant_id, network_id): try: network_dict = { 'network_id': network_id, 'segments': segments, 'network_name': network_name, 'shared': shared_net } self.rpc.create_network(tenant_id, network_dict) except arista_exc.AristaRpcError as err: LOG.error( _LE("create_network_postcommit: Did not create " "network %(name)s. Reason: %(err)s"), { 'name': network_name, 'err': err }) else: LOG.info( _LI('Network %s is not created as it is not found in ' 'Arista DB'), network_id)
def get_host_physnet(self, context): """Returns dictionary which contains physical topology information for a given host_id """ host_id = utils.hostname(context.host) cmd = ['show network physical-topology neighbors'] try: response = self._run_eos_cmds(cmd) # Get response for 'show network physical-topology neighbors' # command neighbors = response[0]['neighbors'] for neighbor in neighbors: if host_id in neighbor: physnet = neighbors[neighbor]['toPort'][0]['hostname'] LOG.debug("get_physical_network: Physical Network for " "%(host)s is %(physnet)s", {'host': host_id, 'physnet': physnet}) return physnet LOG.debug("Physical network not found for %(host)s", {'host': host_id}) except Exception as exc: LOG.error(_LE('command %(cmd)s failed with ' '%(exc)s'), {'cmd': cmd, 'exc': exc}) return None
def create_security_group(self, resource, event, trigger, **kwargs): sg = kwargs.get('security_group') try: self.client.create_security_group(sg) except Exception as e: with excutils.save_and_reraise_exception(): LOG.error( _LE("Failed to create a security group %(sg_id)s " "in Arista Driver: %(err)s"), { "sg_id": sg["id"], "err": e }) try: self.client.delete_security_group(sg) except Exception: LOG.exception(_LE("Failed to delete security group %s"), sg['id'])
def create_router_interfaces(self, router_interfaces): for r in router_interfaces: try: self.driver.add_router_interface(self, r) except Exception: LOG.error(_LE("Error Adding interface %(subnet_id)s " "to router %(router_id)s on Arista HW"), {'subnet_id': r['subnet_id'], 'router_id': r['id']})
def delete_security_group_rule(self, resource, event, trigger, **kwargs): sgr_id = kwargs.get('security_group_rule_id') try: self.client.delete_security_group_rule(sgr_id) except Exception as e: LOG.error( _LE("Failed to delete security group %(sgr_id)s " "rule in Arista Driver: %(err)s"), { "sgr_id": sgr_id, "err": e })
def update_security_group(self, resource, event, trigger, **kwargs): sg = kwargs.get('security_group') try: self.client.update_security_group(sg) except Exception as e: LOG.error( _LE("Failed to update security group %(sg_id)s " "in Arista Driver: %(err)s"), { "sg_id": sg["id"], "err": e })
def delete_router(self, context, router_id): """Delete an existing router from Arista HW as well as from the DB.""" router = self.get_router(context, router_id) # Delete router on the Arista Hw try: self.driver.delete_router(context, router_id, router) except Exception as e: LOG.error(_LE("Error deleting router on Arista HW " "router %(r)s exception=%(e)s"), {'r': router, 'e': e}) super(AristaL3ServicePlugin, self).delete_router(context, router_id)
def add_router_interface(self, context, router_id, interface_info): """Add a subnet of a network to an existing router.""" new_router = super(AristaL3ServicePlugin, self).add_router_interface(context, router_id, interface_info) # Get network info for the subnet that is being added to the router. # Check if the interface information is by port-id or subnet-id add_by_port, add_by_sub = self._validate_interface_info(interface_info) if add_by_sub: subnet = self.get_subnet(context, interface_info['subnet_id']) elif add_by_port: port = self.get_port(context, interface_info['port_id']) subnet_id = port['fixed_ips'][0]['subnet_id'] subnet = self.get_subnet(context, subnet_id) network_id = subnet['network_id'] # To create SVI's in Arista HW, the segmentation Id is required # for this network. ml2_db = NetworkContext(self, context, {'id': network_id}) seg_id = ml2_db.network_segments[0]['segmentation_id'] # Package all the info needed for Hw programming router = super(AristaL3ServicePlugin, self).get_router(context, router_id) router_info = copy.deepcopy(new_router) router_info['seg_id'] = seg_id router_info['name'] = router['name'] router_info['cidr'] = subnet['cidr'] router_info['gip'] = subnet['gateway_ip'] router_info['ip_version'] = subnet['ip_version'] try: self.driver.add_router_interface(context, router_info) return new_router except Exception: with excutils.save_and_reraise_exception(): LOG.error( _LE("Error Adding subnet %(subnet)s to " "router %(router_id)s on Arista HW"), { 'subnet': subnet, 'router_id': router_id }) super(AristaL3ServicePlugin, self).remove_router_interface(context, router_id, interface_info)
def synchronize(self): """Synchronizes Router DB from Neturon DB with EOS. Walks through the Neturon Db and ensures that all the routers created in Netuton DB match with EOS. After creating appropriate routers, it ensures to add interfaces as well. Uses idempotent properties of EOS configuration, which means same commands can be repeated. """ LOG.info(_LI('Syncing Neutron Router DB <-> EOS')) ctx = nctx.get_admin_context() routers = super(AristaL3ServicePlugin, self).get_routers(ctx) for r in routers: tenant_id = r['tenant_id'] ports = self.ndb.get_all_ports_for_tenant(tenant_id) try: self.driver.create_router(self, tenant_id, r) except Exception: continue # Figure out which interfaces are added to this router for p in ports: if p['device_id'] == r['id']: net_id = p['network_id'] subnet_id = p['fixed_ips'][0]['subnet_id'] subnet = self.ndb.get_subnet_info(subnet_id) ml2_db = NetworkContext(self, ctx, {'id': net_id}) seg_id = ml2_db.network_segments[0]['segmentation_id'] r['seg_id'] = seg_id r['cidr'] = subnet['cidr'] r['gip'] = subnet['gateway_ip'] r['ip_version'] = subnet['ip_version'] try: self.driver.add_router_interface(self, r) except Exception: LOG.error( _LE("Error Adding interface %(subnet_id)s " "to router %(router_id)s on Arista HW"), { 'subnet_id': subnet_id, 'router_id': r })
def update_router(self, context, router_id, router): """Update an existing router in DB, and update it in Arista HW.""" # Read existing router record from DB original_router = self.get_router(context, router_id) # Update router DB new_router = super(AristaL3ServicePlugin, self).update_router( context, router_id, router) # Modify router on the Arista Hw try: self.driver.update_router(context, router_id, original_router, new_router) return new_router except Exception: LOG.error(_LE("Error updating router on Arista HW router=%s "), new_router)
def add_router_interface(self, context, router_id, interface_info): """Add a subnet of a network to an existing router.""" new_router = super(AristaL3ServicePlugin, self).add_router_interface( context, router_id, interface_info) core = directory.get_plugin() # Get network info for the subnet that is being added to the router. # Check if the interface information is by port-id or subnet-id add_by_port, add_by_sub = self._validate_interface_info(interface_info) if add_by_sub: subnet = core.get_subnet(context, interface_info['subnet_id']) elif add_by_port: port = core.get_port(context, interface_info['port_id']) subnet_id = port['fixed_ips'][0]['subnet_id'] subnet = core.get_subnet(context, subnet_id) network_id = subnet['network_id'] # To create SVI's in Arista HW, the segmentation Id is required # for this network. ml2_db = NetworkContext(self, context, {'id': network_id}) seg_id = ml2_db.network_segments[0]['segmentation_id'] # Package all the info needed for Hw programming router = self.get_router(context, router_id) router_info = copy.deepcopy(new_router) router_info['seg_id'] = seg_id router_info['name'] = router['name'] router_info['cidr'] = subnet['cidr'] router_info['gip'] = subnet['gateway_ip'] router_info['ip_version'] = subnet['ip_version'] try: self.driver.add_router_interface(context, router_info) return new_router except Exception: with excutils.save_and_reraise_exception(): LOG.error(_LE("Error Adding subnet %(subnet)s to " "router %(router_id)s on Arista HW"), {'subnet': subnet, 'router_id': router_id}) super(AristaL3ServicePlugin, self).remove_router_interface( context, router_id, interface_info)
def create_router(self, context, router): """Create a new router entry in DB, and create it Arista HW.""" tenant_id = router['router']['tenant_id'] # Add router to the DB new_router = super(AristaL3ServicePlugin, self).create_router(context, router) # create router on the Arista Hw try: self.driver.create_router(context, tenant_id, new_router) return new_router except Exception: with excutils.save_and_reraise_exception(): LOG.error(_LE("Error creating router on Arista HW router=%s "), new_router) super(AristaL3ServicePlugin, self).delete_router(context, new_router['id'])
def update_router(self, context, router_id, router): """Update an existing router in DB, and update it in Arista HW.""" # Read existing router record from DB original_router = super(AristaL3ServicePlugin, self).get_router(context, router_id) # Update router DB new_router = super(AristaL3ServicePlugin, self).update_router(context, router_id, router) # Modify router on the Arista Hw try: self.driver.update_router(context, router_id, original_router, new_router) return new_router except Exception: LOG.error(_LE("Error updating router on Arista HW router=%s "), new_router)
def get_physical_network(self, host_id): """Returns dirctionary which contains physical topology information for a given host_id """ fqdns_used = cfg.CONF.ml2_arista['use_fqdn'] physnet = None switch_id = None mac_to_hostname = {} cmds = ['show network physical-topology neighbors', 'show network physical-topology hosts'] try: response = self._run_eos_cmds(cmds) # Get response for 'show network physical-topology neighbors' # command neighbors = response[0]['neighbors'] for neighbor in neighbors: if host_id in neighbor: switchname = neighbors[neighbor]['toPort'][0]['hostname'] physnet = switchname if fqdns_used else ( switchname.split('.')[0]) switch_id = neighbors[neighbor]['toPort'][0].get('hostid') if not switch_id: switch_id = response[1]['hosts'][switchname]['name'] break # Check if the switch is part of an MLAG pair, and lookup the # pair's physnet name if so physnet = self.mlag_pairs.get(physnet, physnet) for host in response[1]['hosts'].values(): mac_to_hostname[host['name']] = host['hostname'] res = {'physnet': physnet, 'switch_id': switch_id, 'mac_to_hostname': mac_to_hostname} LOG.debug("get_physical_network: Physical Network info for " "%(host)s is %(res)s", {'host': host_id, 'res': res}) return res except Exception as exc: LOG.error(_LE('command %(cmds)s failed with ' '%(exc)s'), {'cmds': cmds, 'exc': exc}) return {}
def delete_router(self, context, router_id): """Delete an existing router from Arista HW as well as from the DB.""" router = super(AristaL3ServicePlugin, self).get_router(context, router_id) tenant_id = router['tenant_id'] # Delete router on the Arista Hw try: self.driver.delete_router(context, tenant_id, router_id, router) except Exception as e: LOG.error( _LE("Error deleting router on Arista HW " "router %(r)s exception=%(e)s"), { 'r': router, 'e': e }) super(AristaL3ServicePlugin, self).delete_router(context, router_id)
def create_router(self, context, router): """Create a new router entry in DB, and create it Arista HW.""" # Add router to the DB new_router = super(AristaL3ServicePlugin, self).create_router( context, router) # create router on the Arista Hw try: self.driver.create_router(context, new_router) return new_router except Exception: with excutils.save_and_reraise_exception(): LOG.error(_LE("Error creating router on Arista HW router=%s "), new_router) super(AristaL3ServicePlugin, self).delete_router( context, new_router['id'] )
def delete_network_postcommit(self, context): """Send network delete request to Arista HW.""" network = context.current segments = context.network_segments network_id = network['id'] tenant_id = network['tenant_id'] or constants.INTERNAL_TENANT_ID with self.eos_sync_lock: # Succeed deleting network in case EOS is not accessible. # EOS state will be updated by sync thread once EOS gets # alive. try: self.rpc.delete_network(tenant_id, network_id, segments) # if necessary, delete tenant as well. self.delete_tenant(tenant_id) except arista_exc.AristaRpcError as err: LOG.error( _LE('delete_network_postcommit: Did not delete ' 'network %(network_id)s. Reason: %(err)s'), { 'network_id': network_id, 'err': err })
def get_baremetal_physnet(self, context): """Returns dictionary which contains mac to hostname mapping""" port = context.current host_id = context.host cmd = ['show network physical-topology hosts'] try: response = self._run_eos_cmds(cmd) binding_profile = port.get(portbindings.PROFILE, {}) link_info = binding_profile.get('local_link_information', []) for link in link_info: switch_id = link.get('switch_id') for host in response[0]['hosts'].values(): if switch_id == host['name']: physnet = host['hostname'] LOG.debug("get_physical_network: Physical Network for " "%(host)s is %(physnet)s", {'host': host_id, 'physnet': physnet}) return physnet LOG.debug("Physical network not found for %(host)s", {'host': host_id}) except Exception as exc: LOG.error(_LE('command %(cmd)s failed with ' '%(exc)s'), {'cmd': cmd, 'exc': exc}) return None
def update_port_postcommit(self, context): """Update the name of a given port in EOS. At the moment we only support port name change Any other change to port is not supported at this time. """ port = context.current orig_port = context.original device_id = port['device_id'] device_owner = port['device_owner'] host = context.host is_vm_boot = device_id and device_owner vnic_type = port['binding:vnic_type'] binding_profile = port['binding:profile'] bindings = [] if binding_profile: bindings = binding_profile['local_link_information'] port_id = port['id'] port_name = port['name'] network_id = port['network_id'] tenant_id = port['tenant_id'] or INTERNAL_TENANT_ID # Ensure that we use tenant Id for the network owner tenant_id = self._network_owner_tenant(context, network_id, tenant_id) sg = port['security_groups'] orig_sg = orig_port['security_groups'] pretty_log("update_port_postcommit: new", port) pretty_log("update_port_postcommit: orig", orig_port) # Check if it is port migration case if self._handle_port_migration_postcommit(context): # Return from here as port migration is already handled. return seg_info = self._bound_segments(context) if not seg_info: LOG.debug("Ignoring the update as the port is not managed by " "Arista switches.") return with self.eos_sync_lock: hostname = self._host_name(host) segmentation_id = seg_info[driver_api.SEGMENTATION_ID] port_host_filter = None if (port['device_owner'] == n_const.DEVICE_OWNER_DVR_INTERFACE): # <port, host> uniquely identifies a DVR port. Other # ports are identified by just the port id port_host_filter = host port_provisioned = db_lib.is_port_provisioned( port_id, port_host_filter) # If network does not exist under this tenant, # it may be a shared network. Get shared network owner Id net_provisioned = self._network_provisioned( tenant_id, network_id, segmentation_id=segmentation_id) segments = [] if net_provisioned: if self.rpc.hpb_supported(): for binding_level in context.binding_levels: bound_segment = binding_level.get( driver_api.BOUND_SEGMENT) if bound_segment: segments.append(bound_segment) all_segments = self.ndb.get_all_network_segments( network_id, context=context._plugin_context) try: self.rpc.create_network_segments( tenant_id, network_id, context.network.current['name'], all_segments) except arista_exc.AristaRpcError: LOG.error(_LE("Failed to create network segments")) raise ml2_exc.MechanismDriverError() else: # For non HPB cases, the port is bound to the static # segment segments = self.ndb.get_network_segments(network_id) try: orig_host = context.original_host port_down = False if (port['device_owner'] == n_const.DEVICE_OWNER_DVR_INTERFACE ): # We care about port status only for DVR ports port_down = context.status == n_const.PORT_STATUS_DOWN if orig_host and (port_down or host != orig_host): try: LOG.info("Deleting the port %s" % str(orig_port)) # The port moved to a different host or the VM # connected to the port was deleted or its in DOWN # state. So delete the old port on the old host. self._delete_port(orig_port, orig_host, tenant_id) except ml2_exc.MechanismDriverError: # If deleting a port fails, then not much can be done # about it. Log a warning and move on. LOG.warning(UNABLE_TO_DELETE_PORT_MSG) if (port_provisioned and net_provisioned and hostname and is_vm_boot and not port_down): LOG.info(_LI("Port plugged into network")) # Plug port into the network only if it exists in the db # and is bound to a host and the port is up. self.rpc.plug_port_into_network(device_id, hostname, port_id, network_id, tenant_id, port_name, device_owner, sg, orig_sg, vnic_type, segments=segments, switch_bindings=bindings) else: LOG.info(_LI("Port not plugged into network")) except arista_exc.AristaRpcError as err: LOG.error( _LE('update_port_postcommit: Did not update ' 'port %(port_id)s. Reason: %(err)s'), { 'port_id': port_id, 'err': err })
def bind_port(self, context): """Bind port to a network segment. Provisioning request to Arista Hardware to plug a host into appropriate network is done when the port is created this simply tells the ML2 Plugin that we are binding the port """ host_id = context.host port = context.current physnet_info = {} for segment in context.segments_to_bind: physnet = segment.get(driver_api.PHYSICAL_NETWORK) if not self._is_in_managed_physnets(physnet): LOG.debug( "bind_port for port %(port)s: physical_network " "%(physnet)s is not managed by Arista " "mechanism driver", { 'port': port.get('id'), 'physnet': physnet }) continue # If physnet is not set, we need to look it up using hostname # and topology info if not physnet: if not physnet_info: # We only need to get physnet_info once physnet_info = self.eapi.get_physical_network(host_id) if (port.get('binding:vnic_type') == portbindings.VNIC_BAREMETAL): # Find physnet using link_information in baremetal case physnet = self._get_physnet_from_link_info( port, physnet_info) else: physnet = physnet_info.get('physnet') # If physnet was not found, we cannot bind this port if not physnet: LOG.debug( "bind_port for port %(port)s: no physical_network " "found", {'port': port.get('id')}) continue if segment[driver_api.NETWORK_TYPE] == n_const.TYPE_VXLAN: # The physical network is connected to arista switches, # allocate dynamic segmentation id to bind the port to # the network that the port belongs to. try: next_segment = context.allocate_dynamic_segment({ 'id': context.network.current['id'], 'network_type': n_const.TYPE_VLAN, 'physical_network': physnet }) except Exception as exc: LOG.error( _LE("bind_port for port %(port)s: Failed to " "allocate dynamic segment for physnet " "%(physnet)s. %(exc)s"), { 'port': port.get('id'), 'physnet': physnet, 'exc': exc }) return LOG.debug( "bind_port for port %(port)s: " "current_segment=%(current_seg)s, " "next_segment=%(next_seg)s", { 'port': port.get('id'), 'current_seg': segment, 'next_seg': next_segment }) context.continue_binding(segment['id'], [next_segment]) elif port.get('binding:vnic_type') == portbindings.VNIC_BAREMETAL: # The network_type is vlan, try binding process for baremetal. self._bind_port_to_baremetal(context, segment)
def update_port_postcommit(self, context): """Update the name of a given port in EOS. At the moment we only support port name change Any other change to port is not supported at this time. """ port = context.current orig_port = context.original device_id = port['device_id'] device_owner = port['device_owner'] host = context.host is_vm_boot = device_id and device_owner # When delete a vm, the trunk port context has no device_owner # Keep device_owner as in original port if not device_owner and orig_port.get('trunk_details'): device_owner = orig_port['device_owner'] if not utils.supported_device_owner(device_owner): return vnic_type = port['binding:vnic_type'] binding_profile = port['binding:profile'] bindings = [] if binding_profile: bindings = binding_profile.get('local_link_information', []) port_id = port['id'] port_name = port['name'] network_id = port['network_id'] tenant_id = port['tenant_id'] or constants.INTERNAL_TENANT_ID # Ensure that we use tenant Id for the network owner tenant_id = self._network_owner_tenant(context, network_id, tenant_id) sg = port['security_groups'] orig_sg = orig_port['security_groups'] pretty_log("update_port_postcommit: new", port) pretty_log("update_port_postcommit: orig", orig_port) # Check if it is port migration case if self._handle_port_migration_postcommit(context): # Return from here as port migration is already handled. return # Check if it is trunk_port deletion case seg_info = [] if not port.get('trunk_details') or host: seg_info = self._bound_segments(context) if not seg_info: LOG.debug("Ignoring the update as the port is not managed by " "Arista switches.") return with self.eos_sync_lock: hostname = self._host_name(host) try: orig_host = context.original_host port_down = False if (port['device_owner'] == n_const.DEVICE_OWNER_DVR_INTERFACE or port.get('trunk_details')): # We care about port status only for DVR ports and # trunk ports port_down = context.status == n_const.PORT_STATUS_DOWN if orig_host and (port_down or host != orig_host or device_id == n_const.DEVICE_ID_RESERVED_DHCP_PORT): LOG.info("Deleting the port %s" % str(orig_port)) # The port moved to a different host or the VM # connected to the port was deleted or its in DOWN # state. So delete the old port on the old host. self._delete_port(orig_port, orig_host, tenant_id) if (hostname and is_vm_boot and not port_down and device_id != n_const.DEVICE_ID_RESERVED_DHCP_PORT): segments = seg_info all_segments = self.ndb.get_all_network_segments( network_id, context=context._plugin_context) try: self.rpc.create_network_segments( tenant_id, network_id, context.network.current['name'], all_segments) except arista_exc.AristaRpcError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failed to create network segments")) LOG.info(_LI("Port plugged into network")) # Plug port into the network only if it exists in the db # and is bound to a host and the port is up. trunk_details = port.get('trunk_details') self.rpc.plug_port_into_network( device_id, hostname, port_id, network_id, tenant_id, port_name, device_owner, sg, orig_sg, vnic_type, segments=segments, switch_bindings=bindings, trunk_details=trunk_details) else: LOG.info(_LI("Port not plugged into network")) except arista_exc.AristaRpcError as err: LOG.error( _LE('update_port_postcommit: Did not update ' 'port %(port_id)s. Reason: %(err)s'), { 'port_id': port_id, 'err': err })
def bind_port(self, context): """Bind port to a network segment. Provisioning request to Arista Hardware to plug a host into appropriate network is done when the port is created this simply tells the ML2 Plugin that we are binding the port """ host_id = context.host port = context.current physnet_info = self.eapi.get_physical_network(host_id) physnet = physnet_info.get('physnet') switch_id = physnet_info.get('switch_id') if not physnet or not switch_id: if port.get('binding:vnic_type') == portbindings.VNIC_BAREMETAL: # Find physnet using link_local_information in baremetal case physnet = self._get_physnet_from_link_info(port, physnet_info) else: LOG.debug( "The host %(host)s not connected to arista " "switches. Physical Network info = %(pi)s", { 'host': host_id, 'pi': physnet_info }) return if not physnet or not self._is_in_managed_physnets(physnet): LOG.debug( "bind_port for port %(port)s: physical_network " "%(physnet)s is not managed by Arista " "mechanism driver", { 'port': port.get('id'), 'physnet': physnet }) return LOG.debug( "bind_port for port %(port)s: physical_network=%(physnet)s," "switch_id=%(swid)s", { 'port': port.get('id'), 'physnet': physnet, 'swid': switch_id }) for segment in context.segments_to_bind: if segment[driver_api.NETWORK_TYPE] == p_const.TYPE_VXLAN: # Check if CVX supports HPB if not self.rpc.hpb_supported(): LOG.debug("bind_port: HPB is not supported") return # The physical network is connected to arista switches, # allocate dynamic segmentation id to bind the port to # the network that the port belongs to. try: next_segment = context.allocate_dynamic_segment({ 'id': context.network.current['id'], 'network_type': p_const.TYPE_VLAN, 'physical_network': physnet }) except Exception as exc: LOG.error( _LE("bind_port for port %(port)s: Failed to " "allocate dynamic segment for physnet " "%(physnet)s. %(exc)s"), { 'port': port.get('id'), 'physnet': physnet, 'exc': exc }) return LOG.debug( "bind_port for port %(port)s: " "current_segment=%(current_seg)s, " "next_segment=%(next_seg)s", { 'port': port.get('id'), 'current_seg': segment, 'next_seg': next_segment }) context.continue_binding(segment['id'], [next_segment]) else: # The network_type is vlan, try binding process for baremetal. self._bind_port_to_baremetal(context, segment, physnet_info)