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

PathComp component - Frontend:

- Added logic to ignore mgmt-related links
- Improved composition of subservice config rules
- Cleaned up Constants
- Updated sub-service computation logic
parent 912d24e5
No related branches found
No related tags found
2 merge requests!142Release TeraFlowSDN 2.1,!71OFC'23 + IETF L2VPN Device Driver + Device Controllers + Multiple small improvements
...@@ -15,13 +15,12 @@ ...@@ -15,13 +15,12 @@
import json, logging, requests import json, logging, requests
from typing import Dict, List, Optional, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
from common.proto.context_pb2 import ( from common.proto.context_pb2 import (
ConfigRule, Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, Connection, Device, DeviceList, EndPointId, Link, LinkList, Service, ServiceStatusEnum, ServiceTypeEnum)
ServiceTypeEnum)
from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest
from common.tools.object_factory.ConfigRule import json_config_rule_set
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.EroPathToHops import eropath_to_hops
from .tools.ComposeConfigRules import (
compose_device_config_rules, compose_l2nm_config_rules, compose_l3nm_config_rules, compose_tapi_config_rules)
from .tools.ComposeRequest import compose_device, compose_link, compose_service from .tools.ComposeRequest import compose_device, compose_link, compose_service
from .tools.ComputeSubServices import ( from .tools.ComputeSubServices import (
convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection) convert_explicit_path_hops_to_connections, convert_explicit_path_hops_to_plain_connection)
...@@ -78,6 +77,8 @@ class _Algorithm: ...@@ -78,6 +77,8 @@ class _Algorithm:
def add_links(self, grpc_links : Union[List[Link], LinkList]) -> None: def add_links(self, grpc_links : Union[List[Link], LinkList]) -> None:
if isinstance(grpc_links, LinkList): grpc_links = grpc_links.links if isinstance(grpc_links, LinkList): grpc_links = grpc_links.links
for grpc_link in grpc_links: for grpc_link in grpc_links:
if 'mgmt' in grpc_link.name.lower(): continue
json_link = compose_link(grpc_link) json_link = compose_link(grpc_link)
if len(json_link['link_endpoint_ids']) != 2: continue if len(json_link['link_endpoint_ids']) != 2: continue
self.link_list.append(json_link) self.link_list.append(json_link)
...@@ -148,9 +149,8 @@ class _Algorithm: ...@@ -148,9 +149,8 @@ class _Algorithm:
return connection return connection
def add_service_to_reply( def add_service_to_reply(
self, reply : PathCompReply, context_uuid : str, service_uuid : str, self, reply : PathCompReply, context_uuid : str, service_uuid : str, service_type : ServiceTypeEnum,
device_layer : Optional[DeviceLayerEnum] = None, path_hops : List[Dict] = [], path_hops : List[Dict] = [], config_rules : List = []
config_rules : List = []
) -> Service: ) -> 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
...@@ -159,44 +159,41 @@ class _Algorithm: ...@@ -159,44 +159,41 @@ class _Algorithm:
service_key = (context_uuid, service_uuid) service_key = (context_uuid, service_uuid)
tuple_service = self.service_dict.get(service_key) tuple_service = self.service_dict.get(service_key)
if tuple_service is not None:
service = reply.services.add() service = reply.services.add()
service.CopyFrom(tuple_service[1]) service.service_id.context_id.context_uuid.uuid = context_uuid
service.service_id.service_uuid.uuid = service_uuid
service.service_type = service_type
if service_type == ServiceTypeEnum.SERVICETYPE_L2NM:
compose_l2nm_config_rules(config_rules, service.service_config.config_rules)
elif service_type == ServiceTypeEnum.SERVICETYPE_L3NM:
compose_l3nm_config_rules(config_rules, service.service_config.config_rules)
elif service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE:
compose_tapi_config_rules(config_rules, service.service_config.config_rules)
else: else:
service = reply.services.add() MSG = 'Unhandled generic Config Rules for service {:s} {:s}'
service.service_id.context_id.context_uuid.uuid = context_uuid self.logger.warning(MSG.format(str(service_uuid), str(ServiceTypeEnum.Name(service_type))))
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
if service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE:
json_tapi_settings = {
'capacity_value' : 50.0,
'capacity_unit' : 'GHz',
'layer_proto_name': 'PHOTONIC_MEDIA',
'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC',
'direction' : 'UNIDIRECTIONAL',
}
config_rule = ConfigRule(**json_config_rule_set('/settings', json_tapi_settings))
service.service_config.config_rules.append(config_rule)
else:
service.service_config.config_rules.extend(config_rules)
service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED compose_device_config_rules(config_rules, service.service_config.config_rules, path_hops)
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']
if path_hops is not None and len(path_hops) > 0: egress_endpoint_id = service.service_endpoint_ids.add()
ingress_endpoint_id = service.service_endpoint_ids.add() egress_endpoint_id.device_id.device_uuid.uuid = path_hops[-1]['device']
ingress_endpoint_id.device_id.device_uuid.uuid = path_hops[0]['device'] egress_endpoint_id.endpoint_uuid.uuid = path_hops[-1]['egress_ep']
ingress_endpoint_id.endpoint_uuid.uuid = path_hops[0]['ingress_ep']
egress_endpoint_id = service.service_endpoint_ids.add() if tuple_service is None:
egress_endpoint_id.device_id.device_uuid.uuid = path_hops[-1]['device'] service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED
egress_endpoint_id.endpoint_uuid.uuid = path_hops[-1]['egress_ep'] else:
service.name = tuple_service[1].name
service.service_status.CopyFrom(tuple_service[1].service_status)
service.timestamp.CopyFrom(tuple_service[1].timestamp)
for constraint in tuple_service[1].service_constraints:
service.service_constraints.add().CopyFrom(constraint)
return service return service
...@@ -206,41 +203,54 @@ class _Algorithm: ...@@ -206,41 +203,54 @@ class _Algorithm:
grpc_services : Dict[Tuple[str, str], Service] = {} grpc_services : Dict[Tuple[str, str], Service] = {}
grpc_connections : Dict[str, Connection] = {} grpc_connections : Dict[str, Connection] = {}
for response in response_list: for response in response_list:
service_id = response['serviceId'] orig_service_id = response['serviceId']
context_uuid = service_id['contextId'] context_uuid = orig_service_id['contextId']
service_uuid = service_id['service_uuid'] main_service_uuid = orig_service_id['service_uuid']
service_key = (context_uuid, service_uuid) orig_service_key = (context_uuid, main_service_uuid)
upper_service = self.add_service_to_reply(reply, context_uuid, service_uuid) _,grpc_orig_service = self.service_dict[orig_service_key]
grpc_services[service_key] = upper_service main_service_type = grpc_orig_service.service_type
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:
# no path found: leave connection with no endpoints # no path found: leave connection with no endpoints
# no_path_issue == 1 => no path due to a constraint # no_path_issue == 1 => no path due to a constraint
grpc_services[service_key] = grpc_orig_service
continue continue
orig_config_rules = grpc_orig_service.service_config.config_rules
for service_path_ero in response['path']: for service_path_ero in response['path']:
self.logger.debug('service_path_ero["devices"] = {:s}'.format(str(service_path_ero['devices'])))
_endpoint_to_link_dict = {k:v[0] for k,v in self.endpoint_to_link_dict.items()}
self.logger.debug('self.endpoint_to_link_dict = {:s}'.format(str(_endpoint_to_link_dict)))
path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict) path_hops = eropath_to_hops(service_path_ero['devices'], self.endpoint_to_link_dict)
self.logger.debug('path_hops = {:s}'.format(str(path_hops)))
try: try:
connections = convert_explicit_path_hops_to_connections(path_hops, self.device_dict, service_uuid) _device_dict = {k:v[0] for k,v in self.device_dict.items()}
self.logger.debug('self.device_dict = {:s}'.format(str(_device_dict)))
connections = convert_explicit_path_hops_to_connections(
path_hops, self.device_dict, main_service_uuid, main_service_type)
self.logger.debug('EXTRAPOLATED connections = {:s}'.format(str(connections)))
except: # pylint: disable=bare-except except: # pylint: disable=bare-except
# if not able to extrapolate sub-services and sub-connections, MSG = ' '.join([
# assume single service and single connection 'Unable to Extrapolate sub-services and sub-connections.',
connections = convert_explicit_path_hops_to_plain_connection(path_hops, service_uuid) 'Assuming single-service and single-connection.',
])
self.logger.exception(MSG)
connections = convert_explicit_path_hops_to_plain_connection(
path_hops, main_service_uuid, main_service_type)
self.logger.debug('BASIC connections = {:s}'.format(str(connections)))
for connection in connections: for connection in connections:
connection_uuid,device_layer,path_hops,_ = connection connection_uuid,service_type,path_hops,_ = connection
service_key = (context_uuid, connection_uuid) service_key = (context_uuid, connection_uuid)
grpc_service = grpc_services.get(service_key) grpc_service = self.add_service_to_reply(
if grpc_service is None: reply, context_uuid, connection_uuid, service_type, path_hops=path_hops,
config_rules = upper_service.service_config.config_rules config_rules=orig_config_rules)
grpc_service = self.add_service_to_reply( grpc_services[service_key] = grpc_service
reply, context_uuid, connection_uuid, device_layer=device_layer, path_hops=path_hops,
config_rules=config_rules)
grpc_services[service_key] = grpc_service
for connection in connections: for connection in connections:
connection_uuid,device_layer,path_hops,dependencies = connection connection_uuid,_,path_hops,dependencies = connection
service_key = (context_uuid, connection_uuid) service_key = (context_uuid, connection_uuid)
grpc_service = grpc_services.get(service_key) grpc_service = grpc_services.get(service_key)
...@@ -251,8 +261,8 @@ class _Algorithm: ...@@ -251,8 +261,8 @@ class _Algorithm:
grpc_connection = self.add_connection_to_reply(reply, connection_uuid, grpc_service, path_hops) grpc_connection = self.add_connection_to_reply(reply, connection_uuid, grpc_service, path_hops)
grpc_connections[connection_uuid] = grpc_connection grpc_connections[connection_uuid] = grpc_connection
for service_uuid in dependencies: for sub_service_uuid in dependencies:
sub_service_key = (context_uuid, service_uuid) sub_service_key = (context_uuid, sub_service_uuid)
grpc_sub_service = grpc_services.get(sub_service_key) grpc_sub_service = grpc_services.get(sub_service_key)
if grpc_sub_service is None: if grpc_sub_service is None:
raise Exception('Service({:s}) not found'.format(str(sub_service_key))) raise Exception('Service({:s}) not found'.format(str(sub_service_key)))
......
# 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.
import json, re
from typing import Dict, List, Optional
from common.proto.context_pb2 import ConfigRule
from common.tools.object_factory.ConfigRule import json_config_rule_set
SETTINGS_RULE_NAME = '/settings'
DEV_EP_SETTINGS = re.compile(r'\/device\[([^\]]+)\]\/endpoint\[([^\]]+)\]\/settings')
L2NM_SETTINGS_FIELD_DEFAULTS = {
'encapsulation_type': 'dot1q',
'vlan_id' : 100,
'mtu' : 1450,
}
L3NM_SETTINGS_FIELD_DEFAULTS = {
'encapsulation_type': 'dot1q',
'vlan_id' : 100,
'mtu' : 1450,
}
TAPI_SETTINGS_FIELD_DEFAULTS = {
'capacity_value' : 50.0,
'capacity_unit' : 'GHz',
'layer_proto_name': 'PHOTONIC_MEDIA',
'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC',
'direction' : 'UNIDIRECTIONAL',
}
def find_custom_config_rule(config_rules : List, resource_name : str) -> Optional[Dict]:
resource_value : Optional[Dict] = None
for config_rule in config_rules:
if config_rule.WhichOneof('config_rule') != 'custom': continue
if config_rule.custom.resource_key != resource_name: continue
resource_value = json.loads(config_rule.custom.resource_value)
return resource_value
def compose_config_rules(
main_service_config_rules : List, subservice_config_rules : List, field_defaults : Dict
) -> None:
settings = find_custom_config_rule(main_service_config_rules, SETTINGS_RULE_NAME)
if settings is None: return
json_settings = {}
for field_name,default_value in field_defaults.items():
json_settings[field_name] = settings.get(field_name, default_value)
config_rule = ConfigRule(**json_config_rule_set('/settings', json_settings))
subservice_config_rules.append(config_rule)
def compose_l2nm_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None:
compose_config_rules(main_service_config_rules, subservice_config_rules, L2NM_SETTINGS_FIELD_DEFAULTS)
def compose_l3nm_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None:
compose_config_rules(main_service_config_rules, subservice_config_rules, L3NM_SETTINGS_FIELD_DEFAULTS)
def compose_tapi_config_rules(main_service_config_rules : List, subservice_config_rules : List) -> None:
compose_config_rules(main_service_config_rules, subservice_config_rules, TAPI_SETTINGS_FIELD_DEFAULTS)
def compose_device_config_rules(config_rules : List, subservice_config_rules : List, path_hops : List) -> None:
endpoints_traversed = set()
for path_hop in path_hops:
device_uuid_or_name = path_hop['device']
endpoints_traversed.add((device_uuid_or_name, path_hop['ingress_ep']))
endpoints_traversed.add((device_uuid_or_name, path_hop['egress_ep']))
for config_rule in config_rules:
if config_rule.WhichOneof('config_rule') != 'custom': continue
match = DEV_EP_SETTINGS.match(config_rule.custom.resource_key)
if match is None: continue
device_uuid_or_name = match.group(1)
endpoint_uuid_or_name = match.group(2)
dev_ep_kep = (device_uuid_or_name, endpoint_uuid_or_name)
if dev_ep_kep not in endpoints_traversed: continue
subservice_config_rules.append(config_rule)
...@@ -30,56 +30,176 @@ ...@@ -30,56 +30,176 @@
# ] # ]
# #
# connections=[ # connections=[
# (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), <DeviceLayerEnum.OPTICAL_CONTROLLER: 1>, [ # (UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e'), ServiceTypeEnum.TAPI, [
# {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'} # {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'}
# ], []), # ], []),
# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), <DeviceLayerEnum.PACKET_DEVICE: 30>, [ # (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), ServiceTypeEnum.L2NM, [
# {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'}, # {'device': 'CS1-GW1', 'ingress_ep': '10/1', 'egress_ep': '1/2'},
# {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'}, # {'device': 'TN-R2', 'ingress_ep': '1/2', 'egress_ep': '2/1'},
# {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'}, # {'device': 'TN-R3', 'ingress_ep': '2/1', 'egress_ep': '1/1'},
# {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'} # {'device': 'CS2-GW1', 'ingress_ep': '1/1', 'egress_ep': '10/1'}
# ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]), # ], [UUID('7548edf7-ee7c-4adf-ac0f-c7a0c0dfba8e')]),
# (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), <DeviceLayerEnum.APPLICATION_DEVICE: 40>, [ # (UUID('1e205c82-f6ea-4977-9e97-dc27ef1f4802'), ServiceTypeEnum.L2NM, [
# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'}, # {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'},
# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'} # {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'}
# ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')]) # ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')])
# ] # ]
import queue, uuid import enum, json, queue, uuid
from typing import Dict, List, Tuple from typing import Dict, List, Optional, Tuple
from common.proto.context_pb2 import Device from common.DeviceTypes import DeviceTypeEnum
from .ConstantsMappings import DEVICE_TYPE_TO_LAYER, DeviceLayerEnum from common.proto.context_pb2 import Device, ServiceTypeEnum
class StackActionEnum(enum.Enum):
PATH_INGRESS = 'ingress'
CREATE_CONNECTION = 'create'
APPEND_PATH_HOP = 'append'
CHAIN_CONNECTION = 'chain'
TERMINATE_CONNECTION = 'terminate'
def is_datacenter(dev_type : Optional[DeviceTypeEnum]) -> bool:
return dev_type in {DeviceTypeEnum.DATACENTER, DeviceTypeEnum.EMULATED_DATACENTER}
def is_packet_router(dev_type : Optional[DeviceTypeEnum]) -> bool:
return dev_type in {DeviceTypeEnum.PACKET_ROUTER, DeviceTypeEnum.EMULATED_PACKET_ROUTER}
def is_packet_switch(dev_type : Optional[DeviceTypeEnum]) -> bool:
return dev_type in {DeviceTypeEnum.PACKET_SWITCH, DeviceTypeEnum.EMULATED_PACKET_SWITCH}
def is_packet_device(dev_type : Optional[DeviceTypeEnum]) -> bool:
return is_packet_router(dev_type) or is_packet_switch(dev_type)
def is_tfs_controller(dev_type : Optional[DeviceTypeEnum]) -> bool:
return dev_type in {DeviceTypeEnum.TERAFLOWSDN_CONTROLLER}
def is_mw_controller(dev_type : Optional[DeviceTypeEnum]) -> bool:
return dev_type in {DeviceTypeEnum.MICROWAVE_RADIO_SYSTEM, DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM}
def is_ipm_controller(dev_type : Optional[DeviceTypeEnum]) -> bool:
return dev_type in {DeviceTypeEnum.XR_CONSTELLATION, DeviceTypeEnum.EMULATED_XR_CONSTELLATION}
def is_ols_controller(dev_type : Optional[DeviceTypeEnum]) -> bool:
return dev_type in {DeviceTypeEnum.OPEN_LINE_SYSTEM, DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM}
def is_subdevice(dev_manager : Optional[str]) -> bool:
return dev_manager is not None
def is_subdevice_equal(dev_manager_a : Optional[str], dev_manager_b : Optional[str]) -> bool:
if dev_manager_a is None and dev_manager_b is None: return True
if dev_manager_a is not None and dev_manager_b is not None: return dev_manager_a == dev_manager_b
return False
def get_action(
prv_type : Optional[DeviceTypeEnum], prv_manager : Optional[str],
cur_type : DeviceTypeEnum, cur_manager : Optional[str]
) -> StackActionEnum:
if prv_type is None:
return StackActionEnum.PATH_INGRESS
if is_datacenter(prv_type):
if is_packet_device(cur_type): return StackActionEnum.CREATE_CONNECTION
if is_tfs_controller(cur_type): return StackActionEnum.CREATE_CONNECTION
if is_packet_device(prv_type):
if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION
if is_packet_device(cur_type):
if is_subdevice_equal(cur_manager, prv_manager): return StackActionEnum.APPEND_PATH_HOP
if is_subdevice(prv_manager) and not is_subdevice(cur_manager): return StackActionEnum.TERMINATE_CONNECTION
if not is_subdevice(prv_manager) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION
if is_mw_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION
if is_ols_controller(cur_type) and not is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION
if is_tfs_controller(cur_type) and is_subdevice(cur_manager): return StackActionEnum.CREATE_CONNECTION
if is_mw_controller(prv_type) or is_ols_controller(prv_type):
if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION
if is_tfs_controller(prv_type):
if is_tfs_controller(cur_type) and is_subdevice_equal(prv_manager, cur_manager): return StackActionEnum.APPEND_PATH_HOP
if is_datacenter(cur_type): return StackActionEnum.TERMINATE_CONNECTION
if is_packet_device(cur_type): return StackActionEnum.TERMINATE_CONNECTION
if is_mw_controller(cur_type) or is_ols_controller(cur_type): return StackActionEnum.CHAIN_CONNECTION
str_fields = ', '.join([
'prv_type={:s}'.format(str(prv_type)), 'prv_manager={:s}'.format(str(prv_manager)),
'cur_type={:s}'.format(str(cur_type)), 'cur_manager={:s}'.format(str(cur_manager)),
])
raise Exception('Undefined Action for ({:s})'.format(str_fields))
def get_device_manager_uuid(device : Device) -> Optional[str]:
for config_rule in device.device_config.config_rules:
if config_rule.WhichOneof('config_rule') != 'custom': continue
if config_rule.custom.resource_key != '_manager': continue
device_manager_id = json.loads(config_rule.custom.resource_value)
return device_manager_id['uuid']
return None
def get_device_type(
grpc_device : Device, device_dict : Dict[str, Tuple[Dict, Device]], device_manager_uuid : Optional[str]
) -> DeviceTypeEnum:
if device_manager_uuid is None:
return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member
device_manager_tuple = device_dict.get(device_manager_uuid)
if device_manager_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_manager_uuid)))
_,grpc_device = device_manager_tuple
return DeviceTypeEnum._value2member_map_[grpc_device.device_type] # pylint: disable=no-member
SERVICE_TYPE_LXNM = {ServiceTypeEnum.SERVICETYPE_L3NM, ServiceTypeEnum.SERVICETYPE_L2NM}
def get_service_type(device_type : DeviceTypeEnum, prv_service_type : ServiceTypeEnum) -> ServiceTypeEnum:
if is_tfs_controller(device_type) or is_packet_router(device_type):
if prv_service_type in SERVICE_TYPE_LXNM: return prv_service_type
if is_packet_switch(device_type) or is_mw_controller(device_type):
if prv_service_type == ServiceTypeEnum.SERVICETYPE_L2NM: return prv_service_type
if is_ols_controller(device_type) or is_ipm_controller(device_type):
return ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE
str_fields = ', '.join([
'device_type={:s}'.format(str(device_type)),
])
raise Exception('Undefined Service Type for ({:s})'.format(str_fields))
def convert_explicit_path_hops_to_connections( def convert_explicit_path_hops_to_connections(
path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]], main_connection_uuid : str path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]],
) -> List[Tuple[str, DeviceLayerEnum, List[str], List[str]]]: main_service_uuid : str, main_service_type : ServiceTypeEnum
) -> List[Tuple[str, int, List[str], List[str]]]:
connection_stack = queue.LifoQueue() connection_stack = queue.LifoQueue()
connections : List[Tuple[str, DeviceLayerEnum, List[str], List[str]]] = list() connections : List[Tuple[str, int, List[str], List[str]]] = list()
old_device_layer = None prv_device_uuid = None
last_device_uuid = None prv_device_type = None
prv_manager_uuid = None
for path_hop in path_hops: for path_hop in path_hops:
device_uuid = path_hop['device'] device_uuid = path_hop['device']
if last_device_uuid == device_uuid: continue if prv_device_uuid == device_uuid: continue
device_tuple = device_dict.get(device_uuid) device_tuple = device_dict.get(device_uuid)
if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid))) if device_tuple is None: raise Exception('Device({:s}) not found'.format(str(device_uuid)))
json_device,_ = device_tuple _,grpc_device = device_tuple
device_type = json_device['device_type']
device_layer = DEVICE_TYPE_TO_LAYER.get(device_type) manager_uuid = get_device_manager_uuid(grpc_device)
if device_layer is None: raise Exception('Undefined Layer for DeviceType({:s})'.format(str(device_type))) device_type = get_device_type(grpc_device, device_dict, manager_uuid)
action = get_action(prv_device_type, prv_manager_uuid, device_type, manager_uuid)
if old_device_layer is None:
# path ingress if action == StackActionEnum.PATH_INGRESS:
connection_stack.put((main_connection_uuid, device_layer, [path_hop], [])) connection_stack.put((main_service_uuid, main_service_type, [path_hop], []))
elif old_device_layer > device_layer: elif action == StackActionEnum.CREATE_CONNECTION:
# underlying connection begins
connection_uuid = str(uuid.uuid4()) connection_uuid = str(uuid.uuid4())
connection_stack.put((connection_uuid, device_layer, [path_hop], [])) prv_service_type = connection_stack.queue[-1][1]
elif old_device_layer == device_layer: service_type = get_service_type(device_type, prv_service_type)
# same connection continues connection_stack.put((connection_uuid, service_type, [path_hop], []))
elif action == StackActionEnum.APPEND_PATH_HOP:
connection_stack.queue[-1][2].append(path_hop) connection_stack.queue[-1][2].append(path_hop)
elif old_device_layer < device_layer: elif action == StackActionEnum.CHAIN_CONNECTION:
# underlying connection ended connection = connection_stack.get()
connections.append(connection)
connection_stack.queue[-1][3].append(connection[0])
connection_uuid = str(uuid.uuid4())
prv_service_type = connection_stack.queue[-1][1]
service_type = get_service_type(device_type, prv_service_type)
connection_stack.put((connection_uuid, service_type, [path_hop], []))
elif action == StackActionEnum.TERMINATE_CONNECTION:
connection = connection_stack.get() connection = connection_stack.get()
connections.append(connection) connections.append(connection)
connection_stack.queue[-1][3].append(connection[0]) connection_stack.queue[-1][3].append(connection[0])
...@@ -87,8 +207,9 @@ def convert_explicit_path_hops_to_connections( ...@@ -87,8 +207,9 @@ def convert_explicit_path_hops_to_connections(
else: else:
raise Exception('Uncontrolled condition') raise Exception('Uncontrolled condition')
old_device_layer = device_layer prv_device_uuid = device_uuid
last_device_uuid = device_uuid prv_device_type = device_type
prv_manager_uuid = manager_uuid
# path egress # path egress
connections.append(connection_stack.get()) connections.append(connection_stack.get())
...@@ -96,17 +217,17 @@ def convert_explicit_path_hops_to_connections( ...@@ -96,17 +217,17 @@ def convert_explicit_path_hops_to_connections(
return connections return connections
def convert_explicit_path_hops_to_plain_connection( def convert_explicit_path_hops_to_plain_connection(
path_hops : List[Dict], main_connection_uuid : str path_hops : List[Dict], main_service_uuid : str, main_service_type : ServiceTypeEnum
) -> List[Tuple[str, DeviceLayerEnum, List[str], List[str]]]: ) -> List[Tuple[str, int, List[str], List[str]]]:
connection : Tuple[str, DeviceLayerEnum, List[str], List[str]] = \ connection : Tuple[str, int, List[str], List[str]] = \
(main_connection_uuid, DeviceLayerEnum.PACKET_DEVICE, [], []) (main_service_uuid, main_service_type, [], [])
last_device_uuid = None prv_device_uuid = None
for path_hop in path_hops: for path_hop in path_hops:
device_uuid = path_hop['device'] device_uuid = path_hop['device']
if last_device_uuid == device_uuid: continue if prv_device_uuid == device_uuid: continue
connection[2].append(path_hop) connection[2].append(path_hop)
last_device_uuid = device_uuid prv_device_uuid = device_uuid
return [connection] return [connection]
...@@ -13,8 +13,6 @@ ...@@ -13,8 +13,6 @@
# 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
...@@ -66,50 +64,3 @@ class LinkForwardingDirection(IntEnum): ...@@ -66,50 +64,3 @@ 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.NETWORK.value : DeviceLayerEnum.APPLICATION_DEVICE,
DeviceTypeEnum.EMULATED_PACKET_ROUTER.value : DeviceLayerEnum.PACKET_DEVICE,
DeviceTypeEnum.PACKET_ROUTER.value : DeviceLayerEnum.PACKET_DEVICE,
DeviceTypeEnum.EMULATED_PACKET_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE,
DeviceTypeEnum.PACKET_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE,
DeviceTypeEnum.EMULATED_P4_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE,
DeviceTypeEnum.P4_SWITCH.value : DeviceLayerEnum.MAC_LAYER_DEVICE,
DeviceTypeEnum.EMULATED_MICROWAVE_RADIO_SYSTEM.value : DeviceLayerEnum.MAC_LAYER_CONTROLLER,
DeviceTypeEnum.MICROWAVE_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.XR_CONSTELLATION.value : DeviceLayerEnum.OPTICAL_CONTROLLER,
DeviceTypeEnum.EMULATED_OPTICAL_ROADM.value : DeviceLayerEnum.OPTICAL_DEVICE,
DeviceTypeEnum.OPTICAL_ROADM.value : DeviceLayerEnum.OPTICAL_DEVICE,
DeviceTypeEnum.EMULATED_OPTICAL_TRANSPONDER.value : DeviceLayerEnum.OPTICAL_DEVICE,
DeviceTypeEnum.OPTICAL_TRANSPONDER.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_CONTROLLER.value : ServiceTypeEnum.SERVICETYPE_L2NM,
DeviceLayerEnum.MAC_LAYER_DEVICE.value : ServiceTypeEnum.SERVICETYPE_L2NM,
DeviceLayerEnum.OPTICAL_CONTROLLER.value : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
DeviceLayerEnum.OPTICAL_DEVICE.value : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE,
}
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