Newer
Older
# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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, Tuple, Union
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
def json_to_list(json_str : str) -> List[Union[str, Tuple[str, str]]]:
try:
data = json.loads(json_str)
except: # pylint: disable=bare-except
return [('item', str(json_str))]
if isinstance(data, dict):
return [('kv', (key, value)) for key, value in data.items()]
elif isinstance(data, list):
return [('item', ', '.join(data))]
else:
return [('item', str(data))]
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
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])
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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:
LOGGER.debug("(GetIgpIdFromNodeName)checking update: %s",update.toString())
LOGGER.debug("(GetIgpIdFromNodeName)checking nodes: %s",node.node_name)
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)
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)
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=[]
link_local=[]
link_remote=[]
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)
link_local.append(link)
links_to_node.append(link)
nodes_conected.append(link.local.node_name)
link_remote.append(link)
if(nodes_conected):
LOGGER.debug("(FindConnectedNodes) links to local node:%s",new_node)
LOGGER.debug("(FindConnectedNodes) %s", nodes_conected)
return nodes_conected, link_local, link_remote
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
def getEndpointFromIpInterface(device,ipv4):
"""
Get TFS endpoint uuid drom given device having the IPv4 interface.
"""
for config in device.device_config.config_rules:
if config.WhichOneof('config_rule') == 'custom':
# for item_type, item in json_to_list(config.custom.resource_value):
# if item_type == 'kv':
# # LOGGER.debug("(getEndpointFromIpInterface) item: %s",item)
# endpoint_item=item
# LOGGER.debug("(getEndpointFromIpInterface) config: %s",config.custom.resource_key)
if "/interface" in config.custom.resource_key:
iface=config.custom.resource_key.split("/interface")[1].strip("[]")
LOGGER.debug("(getEndpointFromIpInterface) interface: %s",iface)
if ipv4 in config.custom.resource_value:
LOGGER.debug("(getEndpointFromIpInterface) value: %s",config.custom.resource_value)
resource_dict=json.loads(config.custom.resource_value)
interface = resource_dict['name']
resource_ip=resource_dict['address_ip']
# Search for endpoint uuid assigned to interface
for config in device.device_config.config_rules:
if config.WhichOneof('config_rule') == 'custom':
if "/endpoints/endpoint" in config.custom.resource_key:
key=config.custom.resource_key.split("/endpoints/endpoint")[1].strip("[]")
LOGGER.debug("(getEndpointFromIpInterface) key: %s",key)
if interface in key:
LOGGER.debug("(getEndpointFromIpInterface) value: %s",config.custom.resource_value)
endpoint=config.custom.resource_key.split("/endpoints/endpoint")[1].strip("[]")
resource_dict_endpoint=json.loads(config.custom.resource_value)
return resource_dict_endpoint['uuid'],resource_ip
return None,ipv4