# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # http://www.apache.org/licenses/LICENSE-2.0 # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from typing import List from bgpls_speaker.service.tools.Tools import UpdateRequest,NodeInfo,LinkInfo from common.proto.bgpls_pb2 import NodeDescriptors from common.proto.context_pb2 import ContextId, ContextList,Topology,TopologyId,Device,DeviceDriverEnum,ContextId,Empty, TopologyList from common.Constants import DEFAULT_CONTEXT_NAME from common.tools.object_factory.Context import json_context_id from context.client.ContextClient import ContextClient import logging LOGGER = logging.getLogger(__name__) class DiscoveredDBManager: def __init__(self): self.discoveredDB=[] # Añadir topoDB def AddToDB(self,update_request : UpdateRequest): """ Add BGP Update message to discoveredDB. Checks if node exists in discoveredDB. TODO: check if node exists in context """ # TODO: with self.lock # Check if node info message if(self.checkIfNodeInUpdate(update_request)): # Check if node exists node_count=len(update_request.nodes) for node in update_request.nodes: if(self.CheckIfNodeNameInDb(node) or CheckIfNodeInContext(node.node_name)): # Replace info from node if exists LOGGER.debug("(AddToDB) Node already in DB!!!") update_request.nodes.remove(node) node_count=node_count-1 else: LOGGER.debug("(AddToDB) Node NOT in DB!!!") if(node_count>0): self.discoveredDB.append(update_request) else: # is a link # Compare and update self.discoveredDB.append(update_request) LOGGER.debug("(AddToDB) Actual DB: ") LOGGER.debug("%s", [up.toString() for up in self.discoveredDB]) return True def GetDiscoveredDB(self): return self.discoveredDB def checkIfNodeInUpdate(self,update_request : UpdateRequest): """ Returns true if the update message contains a node info type . """ if(update_request.nodes): return True return False def CheckIfNodeNameInDb(self,new_node : NodeInfo) -> bool: """ Returns true if new node is in the discovered data base already """ for update in self.discoveredDB: for node in update.nodes: if(node.igp_id==new_node.igp_id): return True return False def GetNodeNamesFromDiscoveredDB(self): """ Return a list of node_names from the current discovered devices saved in the discoveredDB """ node_list =[update_request.nodes for update_request in self.discoveredDB if update_request.nodes] # LOGGER.info("nodes (GetNodeNamesFromDiscoveredDB) %s",node_list ) # Inside an update there is a list of nodes , TODO posible FIX: node_info= [node for nodes in node_list for node in nodes] return [node.node_name for node in node_info] def GetNodesFromDiscoveredDB(self): """ Return a list of nodes of class type: tools.NodeInfo from the current discovered devices saved in the discoveredDB. Skips the ones already addded to context. """ node_list =[update_request.nodes for update_request in self.discoveredDB if update_request.nodes] return [node for nodes in node_list for node in nodes if (not CheckIfNodeInContext(node.node_name))] def GetLinksFromDiscoveredDB(self): """ Return a list of links of class type: tools.LinkInfo from the current discovered links saved in the discoveredDB """ link_list= [update_request.links for update_request in self.discoveredDB if update_request.links] return [link for links in link_list for link in links] def UpdateDiscoveredDBWithContext(self): """ Check if device discovered by bgpls is already in the topology. """ # device_names,device_ips=AddContextDevices(context_client) return True def GetNodeNameFromLinkId(self,link_igpid): """ Return the node name given an igp id if exists in the discoveredDB. """ for update in self.discoveredDB: for node in update.nodes: if(node.igp_id==link_igpid): return node.node_name return None def GetIgpIdFromNodeName(self,name): """ Return the IGP ID given a node name if exists in the discoveredDB. """ for update in self.discoveredDB: for node in update.nodes: if(node.node_name==name): return node.igp_id return None def UpdateNodeNameInLink(self): """ Check if the igp id has a node name asigned in the discoveredDB and assign it to the NodeDescriptor name. """ for update in self.discoveredDB: for link in update.links: if(self.GetNodeNameFromLinkId(link.local_id) is not None): LOGGER.info("(UpdateNodeNameInLink) local %s: %s",link.local_id, self.GetNodeNameFromLinkId(link.local_id)) link.local.node_name=self.GetNodeNameFromLinkId(link.local_id) else: link.local.node_name=link.local_id if(self.GetNodeNameFromLinkId(link.remote_id) is not None): LOGGER.info("(UpdateNodeNameInLink) remote %s: %s",link.remote_id, self.GetNodeNameFromLinkId(link.remote_id)) link.remote.node_name=self.GetNodeNameFromLinkId(link.remote_id) else: link.remote.node_name=link.remote_id return True def RemoveLinkFromDB(self): """ Removes a link from the DB if matches the source and the destination. """ return True def FindConnectedNodes(self,new_node): """ Returns a list of nodes connected to the actual node using the discovered link list and comparing de IGP ID. Returns None in case there are no connections. """ # find links where the node appears links_to_node=[] nodes_conected=[] for update in self.discoveredDB: for link in update.links: LOGGER.debug("(FindConnectedNodes) link in up:%s %s", link.local_id, link.remote_id) LOGGER.debug("(FindConnectedNodes) comparing ...:%s",new_node) if(link.local_id == new_node): links_to_node.append(link) nodes_conected.append(link.remote.node_name) if(link.remote_id == new_node): links_to_node.append(link) nodes_conected.append(link.local.node_name) if(nodes_conected): LOGGER.debug("(FindConnectedNodes) links to local node:%s",new_node) LOGGER.debug("(FindConnectedNodes) %s", nodes_conected) return nodes_conected LOGGER.debug("(FindConnectedNodes) NO LINKS TO OTHER NODES") return None def DeleteNodeFromDiscoveredDB(self, node_name) -> bool: """ Deletes a node from de DiscoveredDB given the node name. TODO: igpid¿ """ LOGGER.info("(DeleteNodeFromDiscoveredDB)") for i,update in enumerate(self.discoveredDB): for node in update.nodes: if(node_name==node.node_name): del self.discoveredDB[i] return True def AddContextDevicesFull(context_client : ContextClient) -> bool: """ debug purposes """ LOGGER.info("(AddContextDevices)") contexts : ContextList = context_client.ListContexts(Empty()) for context_ in contexts.contexts: context_uuid : str = context_.context_id.context_uuid.uuid context_name : str = context_.name topologies : TopologyList = context_client.ListTopologies(context_.context_id) # topologies : TopologyList=context_client.ListTopologies(context_client) for topology_ in topologies.topologies: #topology_uuid : str = topology_.topology_id.topology_uuid.uuid topology_name : str = topology_.name context_topology_name = 'Context({:s}):Topology({:s})'.format(context_name, topology_name) # Topos=context.GetTopology(list_topo.topology_id) LOGGER.debug("topo (AddContextDevices) %s",topology_) # details=context_client.GetTopologyDetails(topology_.topology_id) # LOGGER.info("details (AddContextDevices) %s",details) devices=context_client.ListDevices(Empty()) # LOGGER.info("devices (driverSettings) %s",devices) device_names=[] device_ips=[] for device_ in devices.devices: LOGGER.info("device_ (AddContextDevices) %s",device_.name) device_names.append(device_.name) for config_rule_ in device_.device_config.config_rules: if config_rule_.custom.resource_key == "_connect/address": LOGGER.info("device_.resource_value-addr (driverSettings) %s", config_rule_.custom.resource_value) device_ips=config_rule_.custom.resource_value return device_names,device_ips def GetContextDevices(context_client : ContextClient) -> bool: """ Returns de device name and its corresponding device_ip existing in context. """ LOGGER.info("(AddContextDevices)") devices=context_client.ListDevices(Empty()) device_names=[] device_ips=[] for device_ in devices.devices: LOGGER.debug("device_ (AddContextDevices) %s",device_.name) device_names.append(device_.name) for config_rule_ in device_.device_config.config_rules: if config_rule_.custom.resource_key == "_connect/address": # LOGGER.info("device_.resource_value-addr (driverSettings) %s", # config_rule_.custom.resource_value) device_ips=config_rule_.custom.resource_value return device_names,device_ips def CheckIfNodeInContext(node_name) -> bool: """ Returns true if the node exists in the context. """ context_client=ContextClient() context_client.connect() device_names,device_ips=GetContextDevices(context_client) LOGGER.info("(CheckIfNodeInContext) device_names: %s nodena %s",device_names,node_name) for node in device_names: if(node==node_name): LOGGER.info("(CheckIfNodeInContext) Node already in context") return True LOGGER.info("(CheckIfNodeInContext) Node NOT in context") return False