Newer
Older
# 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.
# 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'), ServiceTypeEnum.TAPI, [
# {'device': 'TN-OLS', 'ingress_ep': '833760219d0f', 'egress_ep': 'cf176771a4b9'}
# ], []),
# (UUID('c2e57966-5d82-4705-a5fe-44cf6487219e'), ServiceTypeEnum.L2NM, [
# {'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'), ServiceTypeEnum.L2NM, [
# {'device': 'DC1-GW', 'ingress_ep': 'int', 'egress_ep': 'eth1'},
# {'device': 'DC2-GW', 'ingress_ep': 'eth1', 'egress_ep': 'int'}
# ], [UUID('c2e57966-5d82-4705-a5fe-44cf6487219e')])
# ]
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import enum, json, queue, uuid
from typing import Dict, List, Optional, Tuple
from common.DeviceTypes import DeviceTypeEnum
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(
path_hops : List[Dict], device_dict : Dict[str, Tuple[Dict, Device]],
main_service_uuid : str, main_service_type : ServiceTypeEnum
) -> List[Tuple[str, int, List[str], List[str]]]:
connection_stack = queue.LifoQueue()
connections : List[Tuple[str, int, List[str], List[str]]] = list()
prv_device_uuid = None
prv_device_type = None
prv_manager_uuid = None
for path_hop in path_hops:
device_uuid = path_hop['device']
if prv_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)))
_,grpc_device = device_tuple
manager_uuid = get_device_manager_uuid(grpc_device)
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 action == StackActionEnum.PATH_INGRESS:
connection_stack.put((main_service_uuid, main_service_type, [path_hop], []))
elif action == StackActionEnum.CREATE_CONNECTION:
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.APPEND_PATH_HOP:
connection_stack.queue[-1][2].append(path_hop)
elif action == StackActionEnum.CHAIN_CONNECTION:
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()
connections.append(connection)
connection_stack.queue[-1][3].append(connection[0])
connection_stack.queue[-1][2].append(path_hop)
else:
raise Exception('Uncontrolled condition')
prv_device_uuid = device_uuid
prv_device_type = device_type
prv_manager_uuid = manager_uuid
# path egress
connections.append(connection_stack.get())
assert connection_stack.empty()
return connections
def convert_explicit_path_hops_to_plain_connection(
path_hops : List[Dict], main_service_uuid : str, main_service_type : ServiceTypeEnum
) -> List[Tuple[str, int, List[str], List[str]]]:
connection : Tuple[str, int, List[str], List[str]] = \
(main_service_uuid, main_service_type, [], [])
for path_hop in path_hops:
device_uuid = path_hop['device']
if prv_device_uuid == device_uuid: continue