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

Merge branch 'develop' of https://gitlab.com/teraflow-h2020/controller into...

Merge branch 'develop' of https://gitlab.com/teraflow-h2020/controller into feat/ecoc22-dc-interconnect-disjoint
parents 55fcf40a 0f5789bd
No related branches found
No related tags found
2 merge requests!54Release 2.0.0,!4Compute component:
Showing
with 434 additions and 76 deletions
...@@ -120,7 +120,7 @@ struct map_nodes_t { ...@@ -120,7 +120,7 @@ struct map_nodes_t {
gint numMapNodes; gint numMapNodes;
}; };
#define MAX_NUM_VERTICES 10 // 100 # LGR: reduced from 100 to 10 to divide by 10 the memory used #define MAX_NUM_VERTICES 20 // 100 # LGR: reduced from 100 to 20 to divide by 5 the memory used
#define MAX_NUM_EDGES 10 // 100 # LGR: reduced from 100 to 10 to divide by 10 the memory used #define MAX_NUM_EDGES 10 // 100 # LGR: reduced from 100 to 10 to divide by 10 the memory used
// Structures for the graph composition // Structures for the graph composition
struct targetNodes_t { struct targetNodes_t {
......
...@@ -31,7 +31,7 @@ def main(): ...@@ -31,7 +31,7 @@ def main():
global LOGGER # pylint: disable=global-statement global LOGGER # pylint: disable=global-statement
log_level = get_log_level() log_level = get_log_level()
logging.basicConfig(level=log_level) logging.basicConfig(level=log_level, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s")
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
wait_for_environment_variables([ wait_for_environment_variables([
......
...@@ -14,9 +14,11 @@ ...@@ -14,9 +14,11 @@
import operator import operator
from typing import Dict, List, Optional, Set, Tuple from typing import Dict, List, Optional, Set, Tuple
from common.proto.context_pb2 import Link from common.proto.context_pb2 import Connection, Link, Service
from common.proto.pathcomp_pb2 import Algorithm_KDisjointPath, Algorithm_KShortestPath, PathCompReply, PathCompRequest from common.proto.pathcomp_pb2 import Algorithm_KDisjointPath, Algorithm_KShortestPath, PathCompReply, PathCompRequest
from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.grpc.Tools import grpc_message_to_json_string
from pathcomp.frontend.service.algorithms.tools.ComputeSubServices import convert_explicit_path_hops_to_connections
from pathcomp.frontend.service.algorithms.tools.EroPathToHops import eropath_to_hops
from ._Algorithm import _Algorithm from ._Algorithm import _Algorithm
from .KShortestPathAlgorithm import KShortestPathAlgorithm from .KShortestPathAlgorithm import KShortestPathAlgorithm
...@@ -184,10 +186,47 @@ class KDisjointPathAlgorithm(_Algorithm): ...@@ -184,10 +186,47 @@ class KDisjointPathAlgorithm(_Algorithm):
def get_reply(self) -> PathCompReply: def get_reply(self) -> PathCompReply:
reply = PathCompReply() reply = PathCompReply()
grpc_services : Dict[Tuple[str, str], Service] = {}
grpc_connections : Dict[Tuple[int, str], Connection] = {}
for service_key,paths in self.json_reply.items(): for service_key,paths in self.json_reply.items():
grpc_service = self.add_service_to_reply(reply, service_key[0], service_key[1]) context_uuid, service_uuid = service_key
for path_endpoints in paths:
if path_endpoints is None: continue grpc_services[service_key] = self.add_service_to_reply(reply, context_uuid, service_uuid)
grpc_connection = self.add_connection_to_reply(reply, grpc_service)
self.add_path_to_connection(grpc_connection, path_endpoints) for num_path,service_path_ero in enumerate(paths):
if service_path_ero is None: continue
path_hops = eropath_to_hops(service_path_ero, self.endpoint_to_link_dict)
connections = convert_explicit_path_hops_to_connections(path_hops, self.device_dict, service_uuid)
for connection in connections:
connection_uuid,device_layer,path_hops,_ = connection
service_key = (context_uuid, connection_uuid)
grpc_service = grpc_services.get(service_key)
if grpc_service is not None: continue
grpc_service = self.add_service_to_reply(
reply, context_uuid, connection_uuid, device_layer=device_layer, path_hops=path_hops)
grpc_services[service_key] = grpc_service
for connection in connections:
connection_uuid,device_layer,path_hops,dependencies = connection
service_key = (context_uuid, connection_uuid)
grpc_service = grpc_services.get(service_key)
if grpc_service is None: raise Exception('Service({:s}) not found'.format(str(service_key)))
connection_uuid = '{:s}:{:d}'.format(connection_uuid, num_path)
grpc_connection = grpc_connections.get(connection_uuid)
if grpc_connection is not None: continue
grpc_connection = self.add_connection_to_reply(reply, connection_uuid, grpc_service, path_hops)
grpc_connections[connection_uuid] = grpc_connection
for service_uuid in dependencies:
sub_service_key = (context_uuid, service_uuid)
grpc_sub_service = grpc_services.get(sub_service_key)
if grpc_sub_service is None:
raise Exception('Service({:s}) not found'.format(str(sub_service_key)))
grpc_sub_service_id = grpc_connection.sub_service_ids.add()
grpc_sub_service_id.CopyFrom(grpc_sub_service.service_id)
return reply return reply
...@@ -14,11 +14,14 @@ ...@@ -14,11 +14,14 @@
import json, logging, requests, uuid import json, logging, requests, uuid
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
from common.proto.context_pb2 import Connection, Device, DeviceList, EndPointId, Link, LinkList, Service from common.proto.context_pb2 import Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum
from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest
from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.grpc.Tools import grpc_message_to_json_string
from pathcomp.frontend.Config import BACKEND_URL from pathcomp.frontend.Config import BACKEND_URL
from pathcomp.frontend.service.algorithms.tools.ConstantsMappings import DEVICE_LAYER_TO_SERVICE_TYPE, DeviceLayerEnum
from .tools.EroPathToHops import eropath_to_hops
from .tools.ComposeRequest import compose_device, compose_link, compose_service from .tools.ComposeRequest import compose_device, compose_link, compose_service
from .tools.ComputeSubServices import convert_explicit_path_hops_to_connections
class _Algorithm: class _Algorithm:
def __init__(self, algorithm_id : str, sync_paths : bool, class_name=__name__) -> None: def __init__(self, algorithm_id : str, sync_paths : bool, class_name=__name__) -> None:
...@@ -85,17 +88,17 @@ class _Algorithm: ...@@ -85,17 +88,17 @@ class _Algorithm:
def execute(self, dump_request_filename : Optional[str] = None, dump_reply_filename : Optional[str] = None) -> None: def execute(self, dump_request_filename : Optional[str] = None, dump_reply_filename : Optional[str] = None) -> None:
request = {'serviceList': self.service_list, 'deviceList': self.device_list, 'linkList': self.link_list} request = {'serviceList': self.service_list, 'deviceList': self.device_list, 'linkList': self.link_list}
self.logger.debug('[execute] request={:s}'.format(str(request))) self.logger.info('[execute] request={:s}'.format(str(request)))
if dump_request_filename is not None: if dump_request_filename is not None:
with open(dump_request_filename, 'w', encoding='UTF-8') as f: with open(dump_request_filename, 'w', encoding='UTF-8') as f:
f.write(json.dumps(request, sort_keys=True, indent=4)) f.write(json.dumps(request, sort_keys=True, indent=4))
self.logger.debug('[execute] BACKEND_URL: {:s}'.format(str(BACKEND_URL))) self.logger.info('[execute] BACKEND_URL: {:s}'.format(str(BACKEND_URL)))
reply = requests.post(BACKEND_URL, json=request) reply = requests.post(BACKEND_URL, json=request)
self.status_code = reply.status_code self.status_code = reply.status_code
self.raw_reply = reply.content.decode('UTF-8') self.raw_reply = reply.content.decode('UTF-8')
self.logger.debug('[execute] status_code={:s} reply={:s}'.format(str(reply.status_code), str(self.raw_reply))) self.logger.info('[execute] status_code={:s} reply={:s}'.format(str(reply.status_code), str(self.raw_reply)))
if dump_reply_filename is not None: if dump_reply_filename is not None:
with open(dump_reply_filename, 'w', encoding='UTF-8') as f: with open(dump_reply_filename, 'w', encoding='UTF-8') as f:
f.write('status_code={:s} reply={:s}'.format(str(self.status_code), str(self.raw_reply))) f.write('status_code={:s} reply={:s}'.format(str(self.status_code), str(self.raw_reply)))
...@@ -106,41 +109,77 @@ class _Algorithm: ...@@ -106,41 +109,77 @@ class _Algorithm:
self.json_reply = reply.json() self.json_reply = reply.json()
def add_path_to_connection(self, connection : Connection, path_endpoints : List[Dict]) -> None: def add_connection_to_reply(
for endpoint in path_endpoints: self, reply : PathCompReply, connection_uuid : str, service : Service, path_hops : List[Dict]
device_uuid = endpoint['device_id'] ) -> Connection:
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 = reply.connections.add()
connection.connection_id.connection_uuid.uuid = str(uuid.uuid4())
connection.connection_id.connection_uuid.uuid = connection_uuid
connection.service_id.CopyFrom(service.service_id) connection.service_id.CopyFrom(service.service_id)
return connection
def add_service_to_reply(self, reply : PathCompReply, context_uuid : str, service_uuid : str) -> Service: for path_hop in path_hops:
service_key = (context_uuid, service_uuid) device_uuid = path_hop['device']
tuple_service = self.service_dict.get(service_key)
if tuple_service is None: raise Exception('ServiceKey({:s}) not found'.format(str(service_key))) ingress_endpoint_uuid = path_hop['ingress_ep']
_, grpc_service = tuple_service endpoint_id = connection.path_hops_endpoint_ids.add()
endpoint_id.CopyFrom(self.endpoint_dict[device_uuid][ingress_endpoint_uuid][1])
egress_endpoint_uuid = path_hop['egress_ep']
endpoint_id = connection.path_hops_endpoint_ids.add()
endpoint_id.CopyFrom(self.endpoint_dict[device_uuid][egress_endpoint_uuid][1])
return connection
def add_service_to_reply(
self, reply : PathCompReply, context_uuid : str, service_uuid : str,
device_layer : Optional[DeviceLayerEnum] = None, path_hops : List[Dict] = []
) -> Service:
# TODO: implement support for multi-point services # TODO: implement support for multi-point services
# Control deactivated to enable disjoint paths with multiple redundant endpoints on each side # Control deactivated to enable disjoint paths with multiple redundant endpoints on each side
#service_endpoint_ids = grpc_service.service_endpoint_ids #service_endpoint_ids = service.service_endpoint_ids
#if len(service_endpoint_ids) != 2: raise NotImplementedError('Service must have 2 endpoints') #if len(service_endpoint_ids) != 2: raise NotImplementedError('Service must have 2 endpoints')
service = reply.services.add() service_key = (context_uuid, service_uuid)
service.CopyFrom(grpc_service) tuple_service = self.service_dict.get(service_key)
if tuple_service is not None:
return grpc_service service = reply.services.add()
service.CopyFrom(tuple_service[1])
else:
service = reply.services.add()
service.service_id.context_id.context_uuid.uuid = context_uuid
service.service_id.service_uuid.uuid = service_uuid
if device_layer is not None:
service_type = DEVICE_LAYER_TO_SERVICE_TYPE.get(device_layer.value)
if service_type is None:
MSG = 'Unable to map DeviceLayer({:s}) to ServiceType'
raise Exception(MSG.format(str(device_layer)))
service.service_type = service_type
service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED
if path_hops is not None and len(path_hops) > 0:
ingress_endpoint_id = service.service_endpoint_ids.add()
ingress_endpoint_id.device_id.device_uuid.uuid = path_hops[0]['device']
ingress_endpoint_id.endpoint_uuid.uuid = path_hops[0]['ingress_ep']
egress_endpoint_id = service.service_endpoint_ids.add()
egress_endpoint_id.device_id.device_uuid.uuid = path_hops[-1]['device']
egress_endpoint_id.endpoint_uuid.uuid = path_hops[-1]['egress_ep']
return service
def get_reply(self) -> PathCompReply: def get_reply(self) -> PathCompReply:
response_list = self.json_reply.get('response-list', []) response_list = self.json_reply.get('response-list', [])
reply = PathCompReply() reply = PathCompReply()
grpc_services : Dict[Tuple[str, str], Service] = {}
grpc_connections : Dict[str, Connection] = {}
for response in response_list: for response in response_list:
service_id = response['serviceId'] service_id = response['serviceId']
grpc_service = self.add_service_to_reply(reply, service_id['contextId'], service_id['service_uuid']) context_uuid = service_id['contextId']
service_uuid = service_id['service_uuid']
service_key = (context_uuid, service_uuid)
grpc_services[service_key] = self.add_service_to_reply(reply, context_uuid, service_uuid)
no_path_issue = response.get('noPath', {}).get('issue') no_path_issue = response.get('noPath', {}).get('issue')
if no_path_issue is not None: if no_path_issue is not None:
...@@ -148,9 +187,38 @@ class _Algorithm: ...@@ -148,9 +187,38 @@ class _Algorithm:
# no_path_issue == 1 => no path due to a constraint # no_path_issue == 1 => no path due to a constraint
continue continue
for service_path in response['path']: for service_path_ero in response['path']:
grpc_connection = self.add_connection_to_reply(reply, grpc_service) path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict)
self.add_path_to_connection(grpc_connection, service_path['devices']) connections = convert_explicit_path_hops_to_connections(path_hops, self.device_dict, service_uuid)
for connection in connections:
connection_uuid,device_layer,path_hops,_ = connection
service_key = (context_uuid, connection_uuid)
grpc_service = grpc_services.get(service_key)
if grpc_service is None:
grpc_service = self.add_service_to_reply(
reply, context_uuid, connection_uuid, device_layer=device_layer, path_hops=path_hops)
grpc_services[service_key] = grpc_service
for connection in connections:
connection_uuid,device_layer,path_hops,dependencies = connection
service_key = (context_uuid, connection_uuid)
grpc_service = grpc_services.get(service_key)
if grpc_service is None: raise Exception('Service({:s}) not found'.format(str(service_key)))
grpc_connection = grpc_connections.get(connection_uuid)
if grpc_connection is not None: continue
grpc_connection = self.add_connection_to_reply(reply, connection_uuid, grpc_service, path_hops)
grpc_connections[connection_uuid] = grpc_connection
for service_uuid in dependencies:
sub_service_key = (context_uuid, service_uuid)
grpc_sub_service = grpc_services.get(sub_service_key)
if grpc_sub_service is None:
raise Exception('Service({:s}) not found'.format(str(sub_service_key)))
grpc_sub_service_id = grpc_connection.sub_service_ids.add()
grpc_sub_service_id.CopyFrom(grpc_sub_service.service_id)
# ... "path-capacity": {"total-size": {"value": 200, "unit": 0}}, # ... "path-capacity": {"total-size": {"value": 200, "unit": 0}},
# ... "path-latency": {"fixed-latency-characteristic": "10.000000"}, # ... "path-latency": {"fixed-latency-characteristic": "10.000000"},
......
...@@ -17,7 +17,10 @@ from typing import Dict ...@@ -17,7 +17,10 @@ from typing import Dict
from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
from common.proto.context_pb2 import Constraint, Device, EndPointId, Link, Service, ServiceId, TopologyId from common.proto.context_pb2 import Constraint, Device, EndPointId, Link, Service, ServiceId, TopologyId
from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.grpc.Tools import grpc_message_to_json_string
from .Constants import CapacityUnit, LinkForwardingDirection, LinkPortDirection, TerminationDirection, TerminationState from .ConstantsMappings import (
CapacityUnit, LinkForwardingDirection, LinkPortDirection, TerminationDirection, TerminationState)
LOGGER = logging.getLogger(__name__)
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
......
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# 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.
# Convert the path defined as explicit hops with ingress and egress endpoints per device into a set of connections and
# compute the dependencies among them.
#
# Example:
# o-- int DC1 eth1 -- 10/1 CS1 1/2 -- 1/2 R2 2/1 -- a7.. OLS 60.. -- 2/1 R3 1/1 -- 1/1 CS2 10/1 -- eth1 DC2 int --o
# APP PKT PKT CTRL PKT PKT APP
#
# path_hops = [
# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'},
# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'},
# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'},
# {'device': 'TN-OLS', 'ingress_ep': 'a7a80b23a703', 'egress_ep': '60519106029e'},
# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'},
# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'},
# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'}
# ]
#
# connections=[
# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), <DeviceLayerEnum.OPTICAL_CONTROLLER: 1>, [
# {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'}
# ], []),
# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), <DeviceLayerEnum.PACKET_DEVICE: 30>, [
# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'},
# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'},
# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'},
# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'}
# ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]),
# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), <DeviceLayerEnum.APPLICATION_DEVICE: 40>, [
# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'},
# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'}
# ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')])
# ]
import queue, uuid
from typing import Dict, List, Tuple
from common.proto.context_pb2 import Device
from .ConstantsMappings import DEVICE_TYPE_TO_LAYER, DeviceLayerEnum
def convert_explicit_path_hops_to_connections(
path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], main_connection_uuid : str
) -> List[Tuple[str, DeviceLayerEnum, List[str], List[str]]]:
connection_stack = queue.LifoQueue()
connections : List[Tuple[str, DeviceLayerEnum, List[str], List[str]]] = list()
old_device_layer = None
last_device_uuid = None
for path_hop in path_hops:
device_uuid = path_hop['device']
if last_device_uuid == device_uuid: continue
device_tuple = device_dict.get(device_uuid)
if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid)))
json_device,_ = device_tuple
device_type = json_device['device_type']
device_layer = DEVICE_TYPE_TO_LAYER.get(device_type)
if device_layer is None: raise Exception('Undefined Layer for DeviceType({:s})'.format(str(device_type)))
if old_device_layer is None:
# path ingress
connection_stack.put((main_connection_uuid, device_layer, [path_hop], []))
elif old_device_layer > device_layer:
# underlying connection begins
connection_uuid = str(uuid.uuid4())
connection_stack.put((connection_uuid, device_layer, [path_hop], []))
elif old_device_layer == device_layer:
# same connection continues
connection_stack.queue[-1][2].append(path_hop)
elif old_device_layer < device_layer:
# underlying connection ended
connection = connection_stack.get()
connections.append(connection)
connection_stack.queue[-1][3].append(connection[0])
connection_stack.queue[-1][2].append(path_hop)
else:
raise Exception('Uncontrolled condition')
old_device_layer = device_layer
last_device_uuid = device_uuid
# path egress
connections.append(connection_stack.get())
assert connection_stack.empty()
return connections
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
# limitations under the License. # limitations under the License.
from enum import IntEnum from enum import IntEnum
from common.DeviceTypes import DeviceTypeEnum
from common.proto.context_pb2 import ServiceTypeEnum
class CapacityUnit(IntEnum): class CapacityUnit(IntEnum):
TB = 0 TB = 0
...@@ -64,3 +66,40 @@ class LinkForwardingDirection(IntEnum): ...@@ -64,3 +66,40 @@ class LinkForwardingDirection(IntEnum):
BIDIRECTIONAL = 0 BIDIRECTIONAL = 0
UNIDIRECTIONAL = 1 UNIDIRECTIONAL = 1
UNKNOWN = 2 UNKNOWN = 2
class DeviceLayerEnum(IntEnum):
APPLICATION_CONTROLLER = 41 # Layer 4 domain controller
APPLICATION_DEVICE = 40 # Layer 4 domain device
PACKET_CONTROLLER = 31 # Layer 3 domain controller
PACKET_DEVICE = 30 # Layer 3 domain device
MAC_LAYER_CONTROLLER = 21 # Layer 2 domain controller
MAC_LAYER_DEVICE = 20 # Layer 2 domain device
OPTICAL_CONTROLLER = 1 # Layer 0 domain controller
OPTICAL_DEVICE = 0 # Layer 0 domain device
DEVICE_TYPE_TO_LAYER = {
DeviceTypeEnum.EMULATED_DATACENTER.value : DeviceLayerEnum.APPLICATION_DEVICE,
DeviceTypeEnum.DATACENTER.value : DeviceLayerEnum.APPLICATION_DEVICE,
DeviceTypeEnum.EMULATED_PACKET_ROUTER.value : DeviceLayerEnum.PACKET_DEVICE,
DeviceTypeEnum.PACKET_ROUTER.value : DeviceLayerEnum.PACKET_DEVICE,
DeviceTypeEnum.PACKET_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE,
DeviceTypeEnum.P4_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE,
DeviceTypeEnum.MICROVAWE_RADIO_SYSTEM.value : DeviceLayerEnum.MAC_LAYER_CONTROLLER,
DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value: DeviceLayerEnum.OPTICAL_CONTROLLER,
DeviceTypeEnum.OPEN_LINE_SYSTEM.value : DeviceLayerEnum.OPTICAL_CONTROLLER,
DeviceTypeEnum.OPTICAL_ROADM.value : DeviceLayerEnum.OPTICAL_DEVICE,
DeviceTypeEnum.OPTICAL_TRANDPONDER.value : DeviceLayerEnum.OPTICAL_DEVICE,
}
DEVICE_LAYER_TO_SERVICE_TYPE = {
DeviceLayerEnum.APPLICATION_DEVICE.value: ServiceTypeEnum.SERVICETYPE_L3NM,
DeviceLayerEnum.PACKET_DEVICE.value : ServiceTypeEnum.SERVICETYPE_L3NM,
DeviceLayerEnum.MAC_LAYER_DEVICE.value : ServiceTypeEnum.SERVICETYPE_L2NM,
DeviceLayerEnum.OPTICAL_CONTROLLER.value: ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
}
# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# 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.
# Convert the Explicit Route Object (ERO)-like paths produced by the PathComp component (response['path']) into
# explicit hops with ingress and egress endpoints per device (path_hops).
#
# response['path'] = [{
# 'path-capacity': {'total-size': {'value': 200, 'unit': 0}},
# 'path-latency': {'fixed-latency-characteristic': '12.000000'},
# 'path-cost': {'cost-name': '', 'cost-value': '6.000000', 'cost-algorithm': '0.000000'},
# 'devices': [
# {'device_id': 'DC1-GW', 'endpoint_uuid': 'int'},
# {'device_id': 'DC1-GW', 'endpoint_uuid': 'eth1'},
# {'device_id': 'CS1-GW1', 'endpoint_uuid': '1/2'},
# {'device_id': 'TN-R2', 'endpoint_uuid': '2/1'},
# {'device_id': 'TN-OLS', 'endpoint_uuid': 'ca46812e8ad7'},
# {'device_id': 'TN-R3', 'endpoint_uuid': '1/1'},
# {'device_id': 'CS2-GW1', 'endpoint_uuid': '10/1'},
# {'device_id': 'DC2-GW', 'endpoint_uuid': 'int'}
# ]
# }]
#
# path_hops = [
# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'},
# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'},
# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'},
# {'device': 'TN-OLS', 'ingress_ep': '951f2f57e4a4', 'egress_ep': 'ca46812e8ad7'},
# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'},
# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'},
# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'}
# ]
#
from typing import Dict, List
def eropath_to_hops(ero_path : List[Dict], endpoint_to_link_dict : Dict) -> List[Dict]:
path_hops = []
for endpoint in ero_path:
device_uuid = endpoint['device_id']
endpoint_uuid = endpoint['endpoint_uuid']
if len(path_hops) == 0:
path_hops.append({'device': device_uuid, 'ingress_ep': endpoint_uuid})
continue
last_hop = path_hops[-1]
if (last_hop['device'] == device_uuid):
if ('ingress_ep' not in last_hop) or ('egress_ep' in last_hop): continue
last_hop['egress_ep'] = endpoint_uuid
continue
endpoint_key = (last_hop['device'], last_hop['egress_ep'])
link_tuple = endpoint_to_link_dict.get(endpoint_key)
ingress = next(iter([
ep_id for ep_id in link_tuple[0]['link_endpoint_ids']
if (ep_id['endpoint_id']['device_id'] == device_uuid) and\
(ep_id['endpoint_id']['endpoint_uuid'] != endpoint_uuid)
]), None)
if ingress['endpoint_id']['device_id'] != device_uuid: raise Exception('Malformed path')
path_hops.append({
'device': ingress['endpoint_id']['device_id'],
'ingress_ep': ingress['endpoint_id']['endpoint_uuid'],
'egress_ep': endpoint_uuid,
})
return path_hops
...@@ -17,18 +17,12 @@ from typing import Union ...@@ -17,18 +17,12 @@ from typing import Union
from common.Constants import ServiceNameEnum from common.Constants import ServiceNameEnum
from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name
from common.proto.context_pb2_grpc import add_ContextServiceServicer_to_server from common.proto.context_pb2_grpc import add_ContextServiceServicer_to_server
from common.proto.device_pb2_grpc import add_DeviceServiceServicer_to_server
from common.proto.service_pb2_grpc import add_ServiceServiceServicer_to_server
from common.tests.MockServicerImpl_Context import MockServicerImpl_Context from common.tests.MockServicerImpl_Context import MockServicerImpl_Context
from common.tests.MockServicerImpl_Device import MockServicerImpl_Device
from common.tests.MockServicerImpl_Service import MockServicerImpl_Service
from common.tools.service.GenericGrpcService import GenericGrpcService from common.tools.service.GenericGrpcService import GenericGrpcService
LOCAL_HOST = '127.0.0.1' LOCAL_HOST = '127.0.0.1'
SERVICE_CONTEXT = ServiceNameEnum.CONTEXT SERVICE_CONTEXT = ServiceNameEnum.CONTEXT
SERVICE_DEVICE = ServiceNameEnum.DEVICE
SERVICE_SERVICE = ServiceNameEnum.SERVICE
class MockService_Dependencies(GenericGrpcService): class MockService_Dependencies(GenericGrpcService):
# Mock Service implementing Context, Device, and Service to simplify unitary tests of PathComp # Mock Service implementing Context, Device, and Service to simplify unitary tests of PathComp
...@@ -41,18 +35,6 @@ class MockService_Dependencies(GenericGrpcService): ...@@ -41,18 +35,6 @@ class MockService_Dependencies(GenericGrpcService):
self.context_servicer = MockServicerImpl_Context() self.context_servicer = MockServicerImpl_Context()
add_ContextServiceServicer_to_server(self.context_servicer, self.server) add_ContextServiceServicer_to_server(self.context_servicer, self.server)
self.device_servicer = MockServicerImpl_Device()
add_DeviceServiceServicer_to_server(self.device_servicer, self.server)
self.service_servicer = MockServicerImpl_Service()
add_ServiceServiceServicer_to_server(self.service_servicer, self.server)
def configure_env_vars(self): def configure_env_vars(self):
os.environ[get_env_var_name(SERVICE_CONTEXT, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address) os.environ[get_env_var_name(SERVICE_CONTEXT, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address)
os.environ[get_env_var_name(SERVICE_CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port) os.environ[get_env_var_name(SERVICE_CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port)
os.environ[get_env_var_name(SERVICE_DEVICE, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address)
os.environ[get_env_var_name(SERVICE_DEVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port)
os.environ[get_env_var_name(SERVICE_SERVICE, ENVVAR_SUFIX_SERVICE_HOST )] = str(self.bind_address)
os.environ[get_env_var_name(SERVICE_SERVICE, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(self.bind_port)
...@@ -140,7 +140,7 @@ LINK_TNR2_TNR4_ID, LINK_TNR2_TNR4 = compose_link(DEV_TNR2_EPS[4], DEV_TNR4_EPS[4 ...@@ -140,7 +140,7 @@ LINK_TNR2_TNR4_ID, LINK_TNR2_TNR4 = compose_link(DEV_TNR2_EPS[4], DEV_TNR4_EPS[4
# ----- Service -------------------------------------------------------------------------------------------------------- # ----- Service --------------------------------------------------------------------------------------------------------
SERVICE_DC1GW_DC2GW = compose_service(DEV_DC1GW_EPS[2], DEV_DC2GW_EPS[2], constraints=[ SERVICE_DC1GW_DC2GW = compose_service(DEV_DC1GW_EPS[2], DEV_DC2GW_EPS[2], constraints=[
json_constraint_custom('bandwidth[gbps]', 10.0), json_constraint_custom('bandwidth[gbps]', 10.0),
json_constraint_custom('latency[ms]', 12.0), json_constraint_custom('latency[ms]', 20.0),
]) ])
# ----- Containers ----------------------------------------------------------------------------------------------------- # ----- Containers -----------------------------------------------------------------------------------------------------
......
...@@ -150,7 +150,7 @@ LINK_TNR4_TOLS_ID, LINK_TNR4_TOLS = compose_link(DEV_TNR4_EPS[2], DEV_TOLS_EPS[3 ...@@ -150,7 +150,7 @@ LINK_TNR4_TOLS_ID, LINK_TNR4_TOLS = compose_link(DEV_TNR4_EPS[2], DEV_TOLS_EPS[3
# ----- Service -------------------------------------------------------------------------------------------------------- # ----- Service --------------------------------------------------------------------------------------------------------
SERVICE_DC1GW_DC2GW = compose_service(DEV_DC1GW_EPS[2], DEV_DC2GW_EPS[2], constraints=[ SERVICE_DC1GW_DC2GW = compose_service(DEV_DC1GW_EPS[2], DEV_DC2GW_EPS[2], constraints=[
json_constraint_custom('bandwidth[gbps]', 10.0), json_constraint_custom('bandwidth[gbps]', 10.0),
json_constraint_custom('latency[ms]', 12.0), json_constraint_custom('latency[ms]', 20.0),
]) ])
# ----- Containers ----------------------------------------------------------------------------------------------------- # ----- Containers -----------------------------------------------------------------------------------------------------
......
...@@ -17,7 +17,6 @@ from common.Constants import ServiceNameEnum ...@@ -17,7 +17,6 @@ from common.Constants import ServiceNameEnum
from common.Settings import ( from common.Settings import (
ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_service_port_grpc) ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_service_port_grpc)
from context.client.ContextClient import ContextClient from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient
from pathcomp.frontend.client.PathCompClient import PathCompClient from pathcomp.frontend.client.PathCompClient import PathCompClient
from pathcomp.frontend.service.PathCompService import PathCompService from pathcomp.frontend.service.PathCompService import PathCompService
from pathcomp.frontend.tests.MockService_Dependencies import MockService_Dependencies from pathcomp.frontend.tests.MockService_Dependencies import MockService_Dependencies
...@@ -43,15 +42,7 @@ def context_client(mock_service : MockService_Dependencies): # pylint: disable=r ...@@ -43,15 +42,7 @@ def context_client(mock_service : MockService_Dependencies): # pylint: disable=r
_client.close() _client.close()
@pytest.fixture(scope='session') @pytest.fixture(scope='session')
def device_client(mock_service : MockService_Dependencies): # pylint: disable=redefined-outer-name def pathcomp_service(context_client : ContextClient): # pylint: disable=redefined-outer-name
_client = DeviceClient()
yield _client
_client.close()
@pytest.fixture(scope='session')
def pathcomp_service(
context_client : ContextClient, # pylint: disable=redefined-outer-name
device_client : DeviceClient): # pylint: disable=redefined-outer-name
_service = PathCompService() _service = PathCompService()
_service.start() _service.start()
......
...@@ -28,8 +28,8 @@ from pathcomp.frontend.client.PathCompClient import PathCompClient ...@@ -28,8 +28,8 @@ from pathcomp.frontend.client.PathCompClient import PathCompClient
# Scenarios: # Scenarios:
#from .Objects_A_B_C import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES #from .Objects_A_B_C import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES
from .Objects_DC_CSGW_TN import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES #from .Objects_DC_CSGW_TN import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES
#from .Objects_DC_CSGW_TN_OLS import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES from .Objects_DC_CSGW_TN_OLS import CONTEXTS, DEVICES, LINKS, OBJECTS_PER_TOPOLOGY, SERVICES, TOPOLOGIES
# configure backend environment variables before overwriting them with fixtures to use real backend pathcomp # configure backend environment variables before overwriting them with fixtures to use real backend pathcomp
DEFAULT_PATHCOMP_BACKEND_SCHEME = 'http' DEFAULT_PATHCOMP_BACKEND_SCHEME = 'http'
...@@ -54,18 +54,17 @@ os.environ['PATHCOMP_BACKEND_PORT'] = os.environ.get('PATHCOMP_BACKEND_PORT', ba ...@@ -54,18 +54,17 @@ os.environ['PATHCOMP_BACKEND_PORT'] = os.environ.get('PATHCOMP_BACKEND_PORT', ba
from .PrepareTestScenario import ( # pylint: disable=unused-import from .PrepareTestScenario import ( # pylint: disable=unused-import
# be careful, order of symbols is important here! # be careful, order of symbols is important here!
mock_service, pathcomp_service, context_client, device_client, pathcomp_client) mock_service, pathcomp_service, context_client, pathcomp_client)
LOGGER = logging.getLogger(__name__) LOGGER = logging.getLogger(__name__)
LOGGER.setLevel(logging.DEBUG) LOGGER.setLevel(logging.DEBUG)
def test_prepare_environment( def test_prepare_environment(
context_client : ContextClient, # pylint: disable=redefined-outer-name context_client : ContextClient): # pylint: disable=redefined-outer-name
device_client : DeviceClient): # pylint: disable=redefined-outer-name
for context in CONTEXTS : context_client.SetContext (Context (**context )) for context in CONTEXTS : context_client.SetContext (Context (**context ))
for topology in TOPOLOGIES: context_client.SetTopology(Topology(**topology)) for topology in TOPOLOGIES: context_client.SetTopology(Topology(**topology))
for device in DEVICES : device_client .AddDevice (Device (**device )) for device in DEVICES : context_client.SetDevice (Device (**device ))
for link in LINKS : context_client.SetLink (Link (**link )) for link in LINKS : context_client.SetLink (Link (**link ))
for topology_id, device_ids, link_ids in OBJECTS_PER_TOPOLOGY: for topology_id, device_ids, link_ids in OBJECTS_PER_TOPOLOGY:
...@@ -268,10 +267,9 @@ def test_request_service_kdisjointpath( ...@@ -268,10 +267,9 @@ def test_request_service_kdisjointpath(
def test_cleanup_environment( def test_cleanup_environment(
context_client : ContextClient, # pylint: disable=redefined-outer-name context_client : ContextClient): # pylint: disable=redefined-outer-name
device_client : DeviceClient): # pylint: disable=redefined-outer-name
for link in LINKS : context_client.RemoveLink (LinkId (**link ['link_id' ])) for link in LINKS : context_client.RemoveLink (LinkId (**link ['link_id' ]))
for device in DEVICES : device_client .DeleteDevice (DeviceId (**device ['device_id' ])) for device in DEVICES : context_client.RemoveDevice (DeviceId (**device ['device_id' ]))
for topology in TOPOLOGIES: context_client.RemoveTopology(TopologyId(**topology['topology_id'])) for topology in TOPOLOGIES: context_client.RemoveTopology(TopologyId(**topology['topology_id']))
for context in CONTEXTS : context_client.RemoveContext (ContextId (**context ['context_id' ])) for context in CONTEXTS : context_client.RemoveContext (ContextId (**context ['context_id' ]))
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