Skip to content
Snippets Groups Projects
Commit 92666714 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

PathComp:

- Finished implementation of KDisjointPath algorithm
- Adjusted Algorithm interface
- Moved documentation and example test commands to misc folder
parent 1a85a101
No related branches found
No related tags found
1 merge request!54Release 2.0.0
......@@ -13,7 +13,8 @@
# limitations under the License.
import copy
from typing import Optional
from typing import Dict, List, Optional, Set, Tuple
from common.proto.context_pb2 import Link
from common.proto.pathcomp_pb2 import Algorithm_KDisjointPath, Algorithm_KShortestPath, PathCompReply
from ._Algorithm import _Algorithm
from .KShortestPathAlgorithm import KShortestPathAlgorithm
......@@ -23,6 +24,36 @@ class KDisjointPathAlgorithm(_Algorithm):
super().__init__('KDP', False, class_name=class_name)
self.num_disjoint = algorithm.num_disjoint
def get_link_from_endpoint(self, endpoint : Dict) -> Tuple[Dict, Link]:
device_uuid = endpoint['device_id']
endpoint_uuid = endpoint['endpoint_uuid']
item = self.endpoint_to_link_dict.get((device_uuid, endpoint_uuid))
if item is None:
MSG = 'Link for Endpoint({:s}, {:s}) not found'
self.logger.warning(MSG.format(device_uuid, endpoint_uuid))
return None
return item
def path_to_links(self, path_endpoints : List[Dict]) -> Tuple[List[Dict], Set[str]]:
path_links = list()
path_link_ids = set()
for endpoint in path_endpoints:
link_tuple = self.get_link_from_endpoint(endpoint)
if link_tuple is None: continue
json_link,_ = link_tuple
json_link_id = json_link['link_Id']
if len(path_links) == 0 or path_links[-1]['link_Id'] != json_link_id:
path_links.append(json_link)
path_link_ids.add(json_link_id)
return path_links, path_link_ids
def remove_traversed_links(self, link_list : List[Dict], path_endpoints : List[Dict]):
_, path_link_ids = self.path_to_links(path_endpoints)
new_link_list = list(filter(lambda l: l['link_Id'] not in path_link_ids, link_list))
self.logger.info('cur_link_list = {:s}'.format(str(link_list)))
self.logger.info('new_link_list = {:s}'.format(str(new_link_list)))
return new_link_list
def execute(self, dump_request_filename: Optional[str] = None, dump_reply_filename: Optional[str] = None) -> None:
algorithm = KShortestPathAlgorithm(Algorithm_KShortestPath(k_inspection=0, k_return=1))
algorithm.sync_paths = True
......@@ -35,49 +66,38 @@ class KDisjointPathAlgorithm(_Algorithm):
algorithm.service_list = self.service_list
algorithm.service_dict = self.service_dict
disjoint_paths = dict()
Path = List[Dict]
Path_NoPath = Optional[Path] # None = no path, list = path
self.json_reply : Dict[Tuple[str, str], List[Path_NoPath]] = dict()
for num_path in range(self.num_disjoint):
algorithm.execute('ksp-{:d}-request.json'.format(num_path), 'ksp-{:d}-reply.txt'.format(num_path))
#dump_request_filename = 'ksp-{:d}-request.json'.format(num_path)
#dump_reply_filename = 'ksp-{:d}-reply.txt'.format(num_path)
#algorithm.execute(dump_request_filename, dump_reply_filename)
algorithm.execute()
response_list = algorithm.json_reply.get('response-list', [])
for response in response_list:
service_id = response['serviceId']
service_key = (service_id['contextId'], service_id['service_uuid'])
disjoint_paths_service = disjoint_paths.setdefault(service_key, list())
json_reply_service = self.json_reply.setdefault(service_key, list())
no_path_issue = response.get('noPath', {}).get('issue')
if no_path_issue is not None:
disjoint_paths_service.append(None)
json_reply_service.append(None)
continue
path_endpoints = response['path'][0]['devices']
path_links = list()
path_link_ids = set()
for endpoint in path_endpoints:
device_uuid = endpoint['device_id']
endpoint_uuid = endpoint['endpoint_uuid']
item = algorithm.endpoint_to_link_dict.get((device_uuid, endpoint_uuid))
if item is None:
MSG = 'Link for Endpoint({:s}, {:s}) not found'
self.logger.warning(MSG.format(device_uuid, endpoint_uuid))
continue
json_link,_ = item
json_link_id = json_link['link_Id']
if len(path_links) == 0 or path_links[-1]['link_Id'] != json_link_id:
path_links.append(json_link)
path_link_ids.add(json_link_id)
self.logger.info('path_links = {:s}'.format(str(path_links)))
disjoint_paths_service.append(path_links)
new_link_list = list(filter(lambda l: l['link_Id'] not in path_link_ids, algorithm.link_list))
self.logger.info('algorithm.link_list = {:s}'.format(str(algorithm.link_list)))
self.logger.info('new_link_list = {:s}'.format(str(new_link_list)))
algorithm.link_list = new_link_list
# TODO: find used links and remove them from algorithm.link_list
# TODO: compose disjoint path found
json_reply_service.append(path_endpoints)
algorithm.link_list = self.remove_traversed_links(algorithm.link_list, path_endpoints)
self.logger.info('self.json_reply = {:s}'.format(str(self.json_reply)))
self.logger.info('disjoint_paths = {:s}'.format(str(disjoint_paths)))
self.json_reply = {}
def get_reply(self) -> PathCompReply:
reply = PathCompReply()
for service_key,paths in self.json_reply.items():
grpc_service = self.add_service_to_reply(reply, service_key[0], service_key[1])
for path_endpoints in paths:
if path_endpoints is None: continue
grpc_connection = self.add_connection_to_reply(reply, grpc_service)
self.add_path_to_connection(grpc_connection, path_endpoints)
return reply
......@@ -14,7 +14,7 @@
import json, logging, requests, uuid
from typing import Dict, List, Optional, Tuple
from common.proto.context_pb2 import Device, DeviceList, EndPointId, Link, LinkList, Service
from common.proto.context_pb2 import Connection, Device, DeviceList, EndPointId, Link, LinkList, Service
from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest
from common.tools.grpc.Tools import grpc_message_to_json_string
from pathcomp.frontend.Config import BACKEND_URL
......@@ -32,13 +32,13 @@ class _Algorithm:
self.algorithm_id = algorithm_id
self.sync_paths = sync_paths
self.device_list : List[Device] = list()
self.device_list : List[Dict] = list()
self.device_dict : Dict[str, Tuple[Dict, Device]] = dict()
self.endpoint_dict : Dict[str, Dict[str, Tuple[Dict, EndPointId]]] = dict()
self.link_list : List[Link] = list()
self.link_list : List[Dict] = list()
self.link_dict : Dict[str, Tuple[Dict, Link]] = dict()
self.endpoint_to_link_dict : Dict[Tuple[str, str], Tuple[Dict, Link]] = dict()
self.service_list : List[Service] = list()
self.service_list : List[Dict] = list()
self.service_dict : Dict[Tuple[str, str], Tuple[Dict, Service]] = dict()
def add_devices(self, grpc_devices : DeviceList) -> None:
......@@ -105,26 +105,40 @@ class _Algorithm:
self.json_reply = reply.json()
def add_path_to_connection(self, connection : Connection, path_endpoints : List[Dict]) -> None:
for endpoint in path_endpoints:
device_uuid = endpoint['device_id']
endpoint_uuid = endpoint['endpoint_uuid']
endpoint_id = connection.path_hops_endpoint_ids.add()
endpoint_id.CopyFrom(self.endpoint_dict[device_uuid][endpoint_uuid][1])
def add_connection_to_reply(self, reply : PathCompReply, service : Service) -> Connection:
connection = reply.connections.add()
connection.connection_id.connection_uuid.uuid = str(uuid.uuid4())
connection.service_id.CopyFrom(service.service_id)
return connection
def add_service_to_reply(self, reply : PathCompReply, context_uuid : str, service_uuid : str) -> Service:
service_key = (context_uuid, service_uuid)
tuple_service = self.service_dict.get(service_key)
if tuple_service is None: raise Exception('ServiceKey({:s}) not found'.format(str(service_key)))
_, grpc_service = tuple_service
# TODO: implement support for multi-point services
service_endpoint_ids = grpc_service.service_endpoint_ids
if len(service_endpoint_ids) != 2: raise NotImplementedError('Service must have 2 endpoints')
service = reply.services.add()
service.CopyFrom(grpc_service)
return grpc_service
def get_reply(self) -> PathCompReply:
response_list = self.json_reply.get('response-list', [])
reply = PathCompReply()
for response in response_list:
service_id = response['serviceId']
service_key = (service_id['contextId'], service_id['service_uuid'])
tuple_service = self.service_dict.get(service_key)
if tuple_service is None: raise Exception('ServiceKey({:s}) not found'.format(str(service_key)))
json_service, grpc_service = tuple_service
# TODO: implement support for multi-point services
service_endpoint_ids = grpc_service.service_endpoint_ids
if len(service_endpoint_ids) != 2: raise NotImplementedError('Service must have 2 endpoints')
service = reply.services.add()
service.CopyFrom(grpc_service)
connection = reply.connections.add()
connection.connection_id.connection_uuid.uuid = str(uuid.uuid4())
connection.service_id.CopyFrom(service.service_id)
grpc_service = self.add_service_to_reply(reply, service_id['contextId'], service_id['service_uuid'])
no_path_issue = response.get('noPath', {}).get('issue')
if no_path_issue is not None:
......@@ -132,9 +146,10 @@ class _Algorithm:
# no_path_issue == 1 => no path due to a constraint
continue
service_paths = response['path']
for service_path in response['path']:
grpc_connection = self.add_connection_to_reply(reply, grpc_service)
self.add_path_to_connection(grpc_connection, service_path['devices'])
for service_path in service_paths:
# ... "path-capacity": {"total-size": {"value": 200, "unit": 0}},
# ... "path-latency": {"fixed-latency-characteristic": "10.000000"},
# ... "path-cost": {"cost-name": "", "cost-value": "5.000000", "cost-algorithm": "0.000000"},
......@@ -147,10 +162,4 @@ class _Algorithm:
#path_cost_value = path_cost['cost-value']
#path_cost_algorithm = path_cost['cost-algorithm']
path_endpoints = service_path['devices']
for endpoint in path_endpoints:
device_uuid = endpoint['device_id']
endpoint_uuid = endpoint['endpoint_uuid']
endpoint_id = connection.path_hops_endpoint_ids.add()
endpoint_id.CopyFrom(self.endpoint_dict[device_uuid][endpoint_uuid][1])
return reply
{
"connections": [
{
"connection_id": {"connection_uuid": {"uuid": "d8cb463a-77c4-4149-80fc-32c4842a191d"}},
"path_hops_endpoint_ids": [
{"device_id": {"device_uuid": {"uuid": "DC1-GW"}}, "endpoint_uuid": {"uuid": "int"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "DC1"}}},
{"device_id": {"device_uuid": {"uuid": "DC1-GW"}}, "endpoint_uuid": {"uuid": "eth1"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "DC1"}}},
{"device_id": {"device_uuid": {"uuid": "CS1-GW1"}}, "endpoint_uuid": {"uuid": "200"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "CS1"}}},
{"device_id": {"device_uuid": {"uuid": "TN-R2"}}, "endpoint_uuid": {"uuid": "1"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "TN"}}},
{"device_id": {"device_uuid": {"uuid": "TN-R3"}}, "endpoint_uuid": {"uuid": "100"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "TN"}}},
{"device_id": {"device_uuid": {"uuid": "CS2-GW1"}}, "endpoint_uuid": {"uuid": "1000"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "CS2"}}},
{"device_id": {"device_uuid": {"uuid": "DC2-GW"}}, "endpoint_uuid": {"uuid": "int"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "DC2"}}}
],
"service_id": {
"context_id": {"context_uuid": {"uuid": "admin"}},
"service_uuid": {"uuid": "svc:DC1-GW/int==DC2-GW/int"}
},
"sub_service_ids": []
},
{
"connection_id": {"connection_uuid": {"uuid": "9f7c5075-0736-4d2a-8435-b8e8c37fa6ea"}},
"path_hops_endpoint_ids": [
{"device_id": {"device_uuid": {"uuid": "DC1-GW"}}, "endpoint_uuid": {"uuid": "int"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "DC1"}}},
{"device_id": {"device_uuid": {"uuid": "DC1-GW"}}, "endpoint_uuid": {"uuid": "eth2"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "DC1"}}},
{"device_id": {"device_uuid": {"uuid": "CS1-GW2"}}, "endpoint_uuid": {"uuid": "200"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "CS1"}}},
{"device_id": {"device_uuid": {"uuid": "TN-R1"}}, "endpoint_uuid": {"uuid": "3"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "TN"}}},
{"device_id": {"device_uuid": {"uuid": "TN-R3"}}, "endpoint_uuid": {"uuid": "200"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "TN"}}},
{"device_id": {"device_uuid": {"uuid": "CS2-GW2"}}, "endpoint_uuid": {"uuid": "1000"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "CS2"}}},
{"device_id": {"device_uuid": {"uuid": "DC2-GW"}}, "endpoint_uuid": {"uuid": "int"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "DC2"}}}
],
"service_id": {
"context_id": {"context_uuid": {"uuid": "admin"}},
"service_uuid": {"uuid": "svc:DC1-GW/int==DC2-GW/int"}
},
"sub_service_ids": []
}
],
"services": [
{
"service_id": {
"context_id": {"context_uuid": {"uuid": "admin"}},
"service_uuid": {"uuid": "svc:DC1-GW/int==DC2-GW/int"}
},
"service_type": "SERVICETYPE_L3NM",
"service_endpoint_ids": [
{"device_id": {"device_uuid": {"uuid": "DC1-GW"}}, "endpoint_uuid": {"uuid": "int"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "DC1"}}},
{"device_id": {"device_uuid": {"uuid": "DC2-GW"}}, "endpoint_uuid": {"uuid": "int"},
"topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "DC2"}}}
],
"service_status": {"service_status": "SERVICESTATUS_PLANNED"},
"service_constraints": [
{"custom": {"constraint_type": "bandwidth[gbps]", "constraint_value": "10.0"}},
{"custom": {"constraint_type": "latency[ms]", "constraint_value": "12.0"}}
],
"service_config": {"config_rules": []}
}
]
}
\ No newline at end of file
......@@ -29,3 +29,5 @@ docker network rm teraflowbridge
docker images --filter="dangling=true" --quiet | xargs -r docker rmi
docker exec -i pathcomp bash -c "pytest --log-level=INFO --verbose pathcomp/tests/test_unitary.py"
./scripts/run_tests_locally-pathcomp-frontend.sh
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment