diff --git a/src/service/service/path_computation_element/Enums.py b/src/service/service/path_computation_element/Enums.py deleted file mode 100644 index f55d545b54abf1749e1757200b509562dd4d0455..0000000000000000000000000000000000000000 --- a/src/service/service/path_computation_element/Enums.py +++ /dev/null @@ -1,37 +0,0 @@ -# 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. - -from enum import Enum - -class NodeTypeEnum(Enum): - DEVICE = 'device' - ENDPOINT = 'endpoint' - -class EndPointTypeEnum(Enum): - COPPER = 'copper' - OPTICAL = 'optical' - MICROWAVE = 'microwave' - -class EdgeTypeEnum(Enum): - INTERNAL = 'internal' - COPPER = 'copper' - OPTICAL = 'optical' - MICROWAVE = 'microwave' - VIRTUAL = 'virtual' - OTHER = 'other' - -class LayerTypeEnum(Enum): - COPPER = 'copper' - OPTICAL = 'optical' - MICROWAVE = 'microwave' diff --git a/src/service/service/path_computation_element/PathComputationElement.py b/src/service/service/path_computation_element/PathComputationElement.py deleted file mode 100644 index 80316ac8965ebdbed99264127465488a946f6782..0000000000000000000000000000000000000000 --- a/src/service/service/path_computation_element/PathComputationElement.py +++ /dev/null @@ -1,373 +0,0 @@ -# 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. - -import grpc, json, logging, networkx, uuid -from queue import Queue -from typing import Dict, List, Tuple -from networkx.drawing.nx_pydot import write_dot -from common.Constants import DEFAULT_CONTEXT_UUID -from common.DeviceTypes import DeviceTypeEnum -from common.proto.context_pb2 import ( - ConfigActionEnum, Connection, Device, Empty, EndPoint, EndPointId, Service, ServiceId, ServiceStatusEnum, - ServiceTypeEnum) -from common.tools.grpc.Tools import ( - grpc_message_list_to_json, grpc_message_list_to_json_string, grpc_message_to_json, grpc_message_to_json_string) -from context.client.ContextClient import ContextClient -from .Enums import EdgeTypeEnum, NodeTypeEnum -from .Tools import get_device, get_device_key, get_edge_type, get_endpoint, get_endpoint_key, get_link_key - -LOGGER = logging.getLogger(__name__) -LOGGER.setLevel(logging.INFO) - -SUB_SERVICE_TYPES = { - DeviceTypeEnum.EMULATED_PACKET_ROUTER.value : ServiceTypeEnum.SERVICETYPE_L3NM, - DeviceTypeEnum.EMULATED_OPEN_LINE_SYSTEM.value: ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, - DeviceTypeEnum.PACKET_ROUTER.value : ServiceTypeEnum.SERVICETYPE_L3NM, - DeviceTypeEnum.OPEN_LINE_SYSTEM.value : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, -} -DEFAULT_SUB_SERVICE_TYPE = ServiceTypeEnum.SERVICETYPE_UNKNOWN - -def dump_requirements(requirements): - if requirements is None: return None - return [ - { - 'sub_service': grpc_message_to_json(sub_service), - 'sub_connections': grpc_message_list_to_json(sub_connections), - } - for sub_service,sub_connections in requirements - ] - -def dump_connectivity(connectivity): - if connectivity is None: return None - return { - 'service': grpc_message_to_json(connectivity.get('service')), - 'connections': grpc_message_list_to_json(connectivity.get('connections', [])), - 'requirements': dump_requirements(connectivity.get('requirements', [])), - } - -def dump_hops(hops): - if hops is None: return None - return [ - 'in_endpoint={:s}, device={:s}, out_endpoint={:s}'.format( - grpc_message_to_json_string(in_endpoint), grpc_message_to_json_string(device), - grpc_message_to_json_string(out_endpoint)) - for in_endpoint,device,out_endpoint in hops - ] - -class PathComputationElement: - def __init__(self) -> None: - self.__topology = networkx.Graph() - self.__connectivity = {} # (a_ep_key, z_ep_key) => {service, connection, sub_services: [], sub_connections: []} - self.__service_endpoints = {} # (context_uuid, service_uuid) => (a_ep_key, z_ep_key) - - def dump_topology_to_file(self, dot_filepath : str): - write_dot(self.__topology, dot_filepath) - - def dump_connectivity_to_file(self, filepath : str): - with open(filepath, 'w', encoding='UTF-8') as f: - f.write(str(self.__connectivity)) # do not use json; it contains protobuf objects - - def load_topology(self, context_client : ContextClient): - response_devices = context_client.ListDevices(Empty()) - devices = response_devices.devices - LOGGER.debug('Devices[{:d}] = {:s}'.format(len(devices), grpc_message_to_json_string(response_devices))) - assert len(devices) > 0 - - response_links = context_client.ListLinks(Empty()) - links = response_links.links - LOGGER.debug('Links[{:d}] = {:s}'.format(len(links), grpc_message_to_json_string(response_links))) - assert len(links) > 0 - - for device in response_devices.devices: - device_uuid = get_device_key(device.device_id) - self.__topology.add_node(device_uuid, node_type=NodeTypeEnum.DEVICE, device=device) - for endpoint in device.device_endpoints: - endpoint_key = get_endpoint_key(endpoint.endpoint_id, device_uuid=device_uuid) - self.__topology.add_node(endpoint_key, node_type=NodeTypeEnum.ENDPOINT, endpoint=endpoint) - self.__topology.add_edge(device_uuid, endpoint_key, edge_type=EdgeTypeEnum.INTERNAL) - - for link in response_links.links: - link_uuid = get_link_key(link.link_id) - if len(link.link_endpoint_ids) != 2: - msg = 'Link({:s}) with {:d} != 2 endpoints' - raise NotImplementedError(msg.format(link_uuid, len(link.link_endpoint_ids))) - endpoint_keys = [get_endpoint_key(endpoint_id) for endpoint_id in link.link_endpoint_ids] - edge_type = get_edge_type(self.__topology, endpoint_keys) - self.__topology.add_edge(endpoint_keys[0], endpoint_keys[1], link=link, edge_type=edge_type) - - def load_connectivity(self, context_client : ContextClient, service_id : ServiceId): - pending_service_ids = Queue() - pending_service_ids.put((service_id, True)) - - connectivity = {} - requirements : List[Tuple[Service, List[Connection]]] = connectivity.setdefault('requirements', []) - connections : List[Connection] = connectivity.setdefault('connections', []) - - while not pending_service_ids.empty(): - service_id,is_main = pending_service_ids.get() - - try: - service = context_client.GetService(service_id) - LOGGER.debug('[load_connectivity] GetService({:s}) = {:s}'.format( - grpc_message_to_json_string(service_id), grpc_message_to_json_string(service))) - except grpc.RpcError as e: - if is_main and e.code() == grpc.StatusCode.NOT_FOUND: continue # pylint: disable=no-member - raise - - # TODO: implement support for services with more than 2 endpoints; - # Right now, services with less than 2 endpoints are ignored; with more than 2 endpoints throws exception. - # e.g., compute pairs such as: - # list(filter(lambda ep: ep[0] < ep[1], itertools.product(service_endpoint_ids, service_endpoint_ids))) - service_endpoint_ids = service.service_endpoint_ids - if len(service_endpoint_ids) < 2: continue - if len(service_endpoint_ids) > 2: raise NotImplementedError('Service with more than 2 endpoints') - - service_connections = context_client.ListConnections(service_id) - LOGGER.debug('[load_connectivity] ListConnections({:s}) = {:s}'.format( - grpc_message_to_json_string(service_id), grpc_message_to_json_string(service_connections))) - - if is_main: - connectivity['service'] = service - a_endpoint_key = get_endpoint_key(service_endpoint_ids[0]) - z_endpoint_key = get_endpoint_key(service_endpoint_ids[-1]) - self.__connectivity[(a_endpoint_key, z_endpoint_key)] = connectivity - self.__connectivity[(z_endpoint_key, a_endpoint_key)] = connectivity - context_service_id = (service_id.context_id.context_uuid.uuid, service_id.service_uuid.uuid) - self.__service_endpoints[context_service_id] = (a_endpoint_key, z_endpoint_key) - connections.extend(service_connections.connections) - else: - requirements.append((service, service_connections.connections)) - - for connection in service_connections.connections: - for service_id in connection.sub_service_ids: - pending_service_ids.put((service_id, False)) - - def get_connectivity_from_service_id(self, service_id : ServiceId) -> Dict: - LOGGER.debug('[get_connectivity_from_service_id] service_id={:s}'.format( - grpc_message_to_json_string(service_id))) - - context_uuid = service_id.context_id.context_uuid.uuid - service_uuid = service_id.service_uuid.uuid - context_service_id = (context_uuid, service_uuid) - if context_service_id in self.__service_endpoints: - a_endpoint_key, z_endpoint_key = self.__service_endpoints[context_service_id] - else: - return None - - if (a_endpoint_key, z_endpoint_key) in self.__connectivity: - connectivity = self.__connectivity.get((a_endpoint_key, z_endpoint_key)) - elif (z_endpoint_key, a_endpoint_key) in self.__connectivity: - connectivity = self.__connectivity.get((z_endpoint_key, a_endpoint_key)) - else: - connectivity = None - - if connectivity is None or connectivity.get('connections') is None: return None - LOGGER.debug('[get_connectivity_from_service_id] Connectivity: {:s}'.format( - str(dump_connectivity(connectivity)))) - return connectivity - - def get_connectivity(self, service : Service) -> Tuple[Dict, str, str]: - LOGGER.debug('[get_connectivity] service.service_id = {:s}'.format( - grpc_message_to_json_string(service.service_id))) - - context_uuid = service.service_id.context_id.context_uuid.uuid - service_uuid = service.service_id.service_uuid.uuid - context_service_id = (context_uuid, service_uuid) - - if context_service_id in self.__service_endpoints: - LOGGER.debug('[get_connectivity] exists') - a_endpoint_key, z_endpoint_key = self.__service_endpoints[context_service_id] - - LOGGER.debug('[get_connectivity] a_endpoint_key={:s}'.format(str(a_endpoint_key))) - LOGGER.debug('[get_connectivity] z_endpoint_key={:s}'.format(str(z_endpoint_key))) - else: - LOGGER.debug('[get_connectivity] new') - # TODO: implement support for services with more than 2 endpoints; - # Right now, less than 2 reports None, more than 2 endpoints throws an exception. - # e.g., compute pairs such as: - # list(filter(lambda ep: ep[0] < ep[1], itertools.product(service_endpoint_ids, service_endpoint_ids))) - service_endpoint_ids = service.service_endpoint_ids - - LOGGER.debug('[get_connectivity] service_endpoint_ids[{:d}] = {:s}'.format( - len(service_endpoint_ids), grpc_message_list_to_json_string(service_endpoint_ids))) - - if len(service_endpoint_ids) < 2: return None - if len(service_endpoint_ids) > 2: raise NotImplementedError('Service with more than 2 endpoints') - - a_endpoint_key = get_endpoint_key(service_endpoint_ids[0]) - z_endpoint_key = get_endpoint_key(service_endpoint_ids[-1]) - LOGGER.debug('[get_connectivity] a_endpoint_key={:s}'.format(str(a_endpoint_key))) - LOGGER.debug('[get_connectivity] z_endpoint_key={:s}'.format(str(z_endpoint_key))) - - if (a_endpoint_key, z_endpoint_key) in self.__connectivity: - connectivity = self.__connectivity.get((a_endpoint_key, z_endpoint_key)) - elif (z_endpoint_key, a_endpoint_key) in self.__connectivity: - connectivity = self.__connectivity.get((z_endpoint_key, a_endpoint_key)) - else: - connectivity = None - - LOGGER.debug('[get_connectivity] connectivity = {:s}'.format(str(dump_connectivity(connectivity)))) - - if connectivity is None or connectivity.get('connections') is None: return None, a_endpoint_key, z_endpoint_key - return connectivity, a_endpoint_key, z_endpoint_key - - def route_service(self, service : Service): - if self.__topology is None: raise Exception('Topology has not been loaded') - - connectivity = self.get_connectivity(service) - if connectivity is None: - LOGGER.debug('[route_service] connectivity = None') - return None - _, a_endpoint_key, z_endpoint_key = connectivity - - LOGGER.debug('[route_service] a_endpoint_key={:s}'.format(str(a_endpoint_key))) - LOGGER.debug('[route_service] z_endpoint_key={:s}'.format(str(z_endpoint_key))) - - # TODO: consider implementing something like a K-shortest path instead of a simple shortest path - # pylint: disable=no-value-for-parameter,unexpected-keyword-arg - #paths = networkx.all_shortest_paths(self.__topology, source=a_endpoint_key, target=z_endpoint_key) - path_node_keys = networkx.shortest_path( - self.__topology, source=a_endpoint_key, target=z_endpoint_key) - LOGGER.debug('[route_service] Path[{:d}] = {:s}'.format(len(path_node_keys), str(path_node_keys))) - - if len(path_node_keys) % 3 != 0: - msg = 'Weird path: length({:d}) mod 3 != 0. Path should be a sequence of endpoint-device-endpoint, '\ - ' so it must be multiple of 3' - raise Exception(msg.format(len(path_node_keys))) - - hops : List[Tuple[EndPoint, Device, EndPoint]] = [] - device_type__to__hops : Dict[str, List[int]] = {} - for i in range(0, len(path_node_keys), 3): - hop_device = get_device(self.__topology, path_node_keys[i+1]) - hop_a_endpoint = get_endpoint(self.__topology, path_node_keys[i+0]) - hop_z_endpoint = get_endpoint(self.__topology, path_node_keys[i+2]) - - hop_device_key = get_device_key(hop_device.device_id) - hop_a_endpoint_device_key = get_device_key(hop_a_endpoint.endpoint_id.device_id) - hop_z_endpoint_device_key = get_device_key(hop_z_endpoint.endpoint_id.device_id) - - if hop_a_endpoint_device_key != hop_device_key: - msg = 'Weird path: Hop[{:d}] a_endpoint.device({:s}) != device({:s})' - raise Exception(msg.format(i/3, str(hop_a_endpoint_device_key), str(hop_device_key))) - if hop_z_endpoint_device_key != hop_device_key: - msg = 'Weird path: Hop[{:d}] z_endpoint.device({:s}) != device({:s})' - raise Exception(msg.format(i/3, str(hop_z_endpoint_device_key), str(hop_device_key))) - - hops.append((hop_a_endpoint, hop_device, hop_z_endpoint)) - device_type__to__hops.setdefault(hop_device.device_type, []).append(len(hops) - 1) - - LOGGER.debug('[route_service] hops[{:d}] = {:s}'.format( - len(hops), str(dump_hops(hops)))) - LOGGER.debug('[route_service] device_type__to__hops = {:s}'.format(str(device_type__to__hops))) - - context_uuid = service.service_id.context_id.context_uuid.uuid - service_uuid = service.service_id.service_uuid.uuid - - # create main service's connection - main_service_device_type = hops[0][1].device_type - main_service_hop_indexes = device_type__to__hops.pop(main_service_device_type) - - # create sub-service and sub-services' connections - sub_service_ids = [] - requirements : List[Tuple[Service, List[Connection]]] = [] - for sub_service_device_type, sub_service_hop_indexes in device_type__to__hops.items(): - LOGGER.debug('[route_service] sub_service_device_type = {:s}'.format(str(sub_service_device_type))) - LOGGER.debug('[route_service] sub_service_hop_indexes = {:s}'.format(str(sub_service_hop_indexes))) - - # create sub-service - sub_service_uuid = '{:s}:optical'.format(service_uuid) - sub_service_id = { - 'context_id': {'context_uuid': {'uuid': context_uuid}}, - 'service_uuid': {'uuid': sub_service_uuid}, - } - sub_service = Service(**{ - 'service_id': sub_service_id, - 'service_type' : SUB_SERVICE_TYPES.get(sub_service_device_type, DEFAULT_SUB_SERVICE_TYPE), - 'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED}, - 'service_endpoint_ids': [ - hops[sub_service_hop_indexes[ 0]][ 0].endpoint_id, - hops[sub_service_hop_indexes[-1]][-1].endpoint_id, - ], - 'service_config': {'config_rules': [ - { - 'action': ConfigActionEnum.CONFIGACTION_SET, - 'custom': { - 'resource_key': 'settings', - 'resource_value': json.dumps({ - 'capacity_value': 1, - 'capacity_unit': 'GHz', - 'layer_proto_name': 'PHOTONIC_MEDIA', - 'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC', - 'direction': 'UNIDIRECTIONAL', - }, sort_keys=True), - } - } - ]}, - }) - LOGGER.debug('[route_service] sub_service = {:s}'.format(grpc_message_to_json_string(sub_service))) - - # create sub-service's connection - sub_connection_uuid = '{:s}:{:s}'.format(sub_service_uuid, sub_service_device_type) - sub_conn_path_hops = [] - for i in sub_service_hop_indexes: - sub_conn_path_hops.extend([hops[i][0].endpoint_id, hops[i][2].endpoint_id]) - sub_connection = Connection(**{ - 'connection_id': {'connection_uuid': {'uuid': sub_connection_uuid}}, - 'service_id': sub_service_id, - 'path_hops_endpoint_ids': sub_conn_path_hops, - }) - - LOGGER.debug('[route_service] sub_connection = {:s}'.format(grpc_message_to_json_string(sub_connection))) - - sub_service_ids.append(sub_service_id) - requirements.append((sub_service, [sub_connection])) - - LOGGER.debug('[route_service] sub_service_ids = {:s}'.format(str(sub_service_ids))) - LOGGER.debug('[route_service] requirements = {:s}'.format(str(dump_requirements(requirements)))) - LOGGER.debug('[route_service] requirements[{:d}] = {:s}'.format( - len(requirements), str(dump_requirements(requirements)))) - - connections : List[Connection] = [] - - connection_uuid = '{:s}:{:s}'.format(service_uuid, main_service_device_type) - connection_path_hops : List[EndPointId] = [] - for i in main_service_hop_indexes: - connection_path_hops.extend([hops[i][0].endpoint_id, hops[i][2].endpoint_id]) - connection = Connection(**{ - 'connection_id': {'connection_uuid': {'uuid': connection_uuid}}, - 'service_id': grpc_message_to_json(service.service_id), - 'path_hops_endpoint_ids': connection_path_hops, - 'sub_service_ids': sub_service_ids, - }) - LOGGER.debug('[route_service] connection = {:s}'.format(grpc_message_to_json_string(connection))) - connections.append(connection) - - LOGGER.debug('[route_service] connections[{:d}] = {:s}'.format( - len(connections), grpc_message_list_to_json_string(connections))) - - connectivity = { - 'service': service, - 'connections': connections, - 'requirements': requirements, - } - - LOGGER.debug('[route_service] connectivity = {:s}'.format(str(dump_connectivity(connectivity)))) - - self.__connectivity[(a_endpoint_key, z_endpoint_key)] = connectivity - self.__connectivity[(z_endpoint_key, a_endpoint_key)] = connectivity - - context_service_id = (context_uuid, service_uuid) - self.__service_endpoints[context_service_id] = (a_endpoint_key, z_endpoint_key) - - return connectivity diff --git a/src/service/service/path_computation_element/Tools.py b/src/service/service/path_computation_element/Tools.py deleted file mode 100644 index d8ebdd36dcd5511bd1031a31cafa047d9e04fccf..0000000000000000000000000000000000000000 --- a/src/service/service/path_computation_element/Tools.py +++ /dev/null @@ -1,43 +0,0 @@ -# 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. - -import networkx -from typing import List, Optional -from common.proto.context_pb2 import Device, DeviceId, EndPoint, EndPointId, LinkId -from .Enums import EdgeTypeEnum - -def get_device_key(device_id : DeviceId) -> str: - return device_id.device_uuid.uuid # pylint: disable=no-member - -def get_endpoint_key(endpoint_id : EndPointId, device_uuid : Optional[str] = None) -> str: - if device_uuid is None: device_uuid = endpoint_id.device_id.device_uuid.uuid # pylint: disable=no-member - endpoint_uuid = endpoint_id.endpoint_uuid.uuid # pylint: disable=no-member - return '{:s}/{:s}'.format(device_uuid, endpoint_uuid) - -def get_link_key(link_id : LinkId) -> str: - return link_id.link_uuid.uuid # pylint: disable=no-member - -def get_device(topology : networkx.Graph, device_key : str) -> Device: - return topology.nodes[device_key]['device'] - -def get_endpoint(topology : networkx.Graph, endpoint_key : str) -> EndPoint: - return topology.nodes[endpoint_key]['endpoint'] - -def get_edge_type(topology : networkx.Graph, endpoint_keys : List[str]) -> EdgeTypeEnum: - # pylint: disable=no-member,protected-access - endpoint_types = {get_endpoint(topology, endpoint_key).endpoint_type for endpoint_key in endpoint_keys} - edge_type = None if len(endpoint_types) > 1 else \ - EdgeTypeEnum._value2member_map_.get(endpoint_types.pop()) - if edge_type is None: edge_type = EdgeTypeEnum.OTHER - return edge_type diff --git a/src/service/service/path_computation_element/TopologyViews.py b/src/service/service/path_computation_element/TopologyViews.py deleted file mode 100644 index e9161dc0d4e491d8fdfa239b8603fbf6613967ff..0000000000000000000000000000000000000000 --- a/src/service/service/path_computation_element/TopologyViews.py +++ /dev/null @@ -1,57 +0,0 @@ -# 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. - -import functools, networkx -from typing import List, Optional, Union -from common.proto.context_pb2 import EndPoint -from context.service.database.EndPointModel import EndPointModel -from .Enums import EdgeTypeEnum, LayerTypeEnum -#from .Tools import get_endpoint - -def select_copper_edges(topology, n1, n2): - selected_edges = {EdgeTypeEnum.COPPER, EdgeTypeEnum.INTERNAL, EdgeTypeEnum.OTHER} - return topology[n1][n2].get('edge_type', EdgeTypeEnum.OTHER) in selected_edges - -def select_optical_edges(topology, n1, n2): - selected_edges = {EdgeTypeEnum.OPTICAL, EdgeTypeEnum.INTERNAL, EdgeTypeEnum.OTHER} - return topology[n1][n2].get('edge_type', EdgeTypeEnum.OTHER) in selected_edges - -def select_microwave_edges(topology, n1, n2): - selected_edges = {EdgeTypeEnum.MICROWAVE, EdgeTypeEnum.INTERNAL, EdgeTypeEnum.OTHER} - return topology[n1][n2].get('edge_type', EdgeTypeEnum.OTHER) in selected_edges - -SELECT_LAYER = { - LayerTypeEnum.COPPER : select_copper_edges, - LayerTypeEnum.OPTICAL : select_optical_edges, - LayerTypeEnum.MICROWAVE : select_microwave_edges, -} - -def get_layer(topology : networkx.Graph, layer : LayerTypeEnum): - filter_edge = functools.partial(SELECT_LAYER[layer], topology) - return networkx.subgraph_view(topology, filter_edge=filter_edge) - -def select_layer_from_endpoints(endpoints : List[Union[EndPointModel, EndPoint]]) -> Optional[LayerTypeEnum]: - endpoint_types = set() - for endpoint in endpoints: endpoint_types.add(endpoint.endpoint_type) - if len(endpoint_types) != 1: return None - # pylint: disable=no-member,protected-access - return LayerTypeEnum._value2member_map_.get(endpoint_types.pop()) - -#a_endpoint = get_endpoint(self.__topology, a_endpoint_key) -#z_endpoint = get_endpoint(self.__topology, z_endpoint_key) -#endpoints = [a_endpoint, z_endpoint] -#layer_type = select_layer_from_endpoints(endpoints) -#topology = self.__topology if layer_type is None else get_layer(self.__topology, layer_type) -#write_dot(topology, '../data/layer-{:s}.dot'.format('all' if layer_type is None else str(layer_type.value))) - diff --git a/src/service/service/path_computation_element/__init__.py b/src/service/service/path_computation_element/__init__.py deleted file mode 100644 index 70a33251242c51f49140e596b8208a19dd5245f7..0000000000000000000000000000000000000000 --- a/src/service/service/path_computation_element/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# 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. - diff --git a/src/service/service/service_handlers/l3nm_emulated/ConfigRulesOld.py b/src/service/service/service_handlers/l3nm_emulated/ConfigRulesOld.py deleted file mode 100644 index 8b12049bae40829ed329c3509bdeaedbf55badb4..0000000000000000000000000000000000000000 --- a/src/service/service/service_handlers/l3nm_emulated/ConfigRulesOld.py +++ /dev/null @@ -1,109 +0,0 @@ - - # json_endpoint_settings : Dict = endpoint_settings.value - # #router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' - # route_distinguisher = json_endpoint_settings.get('route_distinguisher', '0:0' ) # '60001:801' - # sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 - # vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 - # address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' - # address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 - # if_subif_name = '{:s}.{:d}'.format(endpoint_uuid, vlan_id) - - # db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True) - # device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) - # json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True) - # json_device_config : Dict = json_device.setdefault('device_config', {}) - # json_device_config_rules : List = json_device_config.setdefault('config_rules', []) - # json_device_config_rules.extend([ - # json_config_rule_set( - # '/network_instance[{:s}]'.format(network_instance_name), { - # 'name': network_instance_name, 'description': network_interface_desc, 'type': 'L3VRF', - # 'route_distinguisher': route_distinguisher, - # #'router_id': router_id, 'address_families': address_families, - # }), - # json_config_rule_set( - # '/interface[{:s}]'.format(endpoint_uuid), { - # 'name': endpoint_uuid, 'description': network_interface_desc, 'mtu': mtu, - # }), - # json_config_rule_set( - # '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), { - # 'name': endpoint_uuid, 'index': sub_interface_index, - # 'description': network_subinterface_desc, 'vlan_id': vlan_id, - # 'address_ip': address_ip, 'address_prefix': address_prefix, - # }), - # json_config_rule_set( - # '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), { - # 'name': network_instance_name, 'id': if_subif_name, 'interface': endpoint_uuid, - # 'subinterface': sub_interface_index, - # }), - # json_config_rule_set( - # '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), { - # 'name': network_instance_name, 'identifier': 'BGP', 'protocol_name': 'BGP', 'as': bgp_as, - # }), - # json_config_rule_set( - # '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { - # 'name': network_instance_name, 'src_protocol': 'STATIC', 'dst_protocol': 'BGP', - # 'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE', - # }), - # json_config_rule_set( - # '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format( - # network_instance_name), { - # 'name': network_instance_name, 'src_protocol': 'DIRECTLY_CONNECTED', 'dst_protocol': 'BGP', - # 'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE', - # }), - # json_config_rule_set( - # '/routing_policy/bgp_defined_set[{:s}_rt_import]'.format(network_instance_name), { - # 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), - # }), - # json_config_rule_set( - # '/routing_policy/bgp_defined_set[{:s}_rt_import][route-target:{:s}]'.format( - # network_instance_name, bgp_route_target), { - # 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), - # 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), - # }), - # json_config_rule_set( - # '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), { - # 'policy_name': '{:s}_import'.format(network_instance_name), - # }), - # json_config_rule_set( - # '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format( - # network_instance_name, '3'), { - # 'policy_name': '{:s}_import'.format(network_instance_name), 'statement_name': '3', - # 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), - # 'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE', - # }), - # json_config_rule_set( - # # pylint: disable=duplicate-string-formatting-argument - # '/network_instance[{:s}]/inter_instance_policies[{:s}_import]'.format( - # network_instance_name, network_instance_name), { - # 'name': network_instance_name, 'import_policy': '{:s}_import'.format(network_instance_name), - # }), - # json_config_rule_set( - # '/routing_policy/bgp_defined_set[{:s}_rt_export]'.format(network_instance_name), { - # 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), - # }), - # json_config_rule_set( - # '/routing_policy/bgp_defined_set[{:s}_rt_export][route-target:{:s}]'.format( - # network_instance_name, bgp_route_target), { - # 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), - # 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), - # }), - # json_config_rule_set( - # '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), { - # 'policy_name': '{:s}_export'.format(network_instance_name), - # }), - # json_config_rule_set( - # '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format( - # network_instance_name, '3'), { - # 'policy_name': '{:s}_export'.format(network_instance_name), 'statement_name': '3', - # 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), - # 'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE', - # }), - # json_config_rule_set( - # # pylint: disable=duplicate-string-formatting-argument - # '/network_instance[{:s}]/inter_instance_policies[{:s}_export]'.format( - # network_instance_name, network_instance_name), { - # 'name': network_instance_name, 'export_policy': '{:s}_export'.format(network_instance_name), - # }), - # ]) - # self.__device_client.ConfigureDevice(Device(**json_device)) - # results.append(True) diff --git a/src/service/service/service_handlers/l3nm_openconfig/ConfigRules.py b/src/service/service/service_handlers/l3nm_openconfig/ConfigRules.py new file mode 100644 index 0000000000000000000000000000000000000000..3a5aff5884c72f1384666a223a3b07da6d4ae4ec --- /dev/null +++ b/src/service/service/service_handlers/l3nm_openconfig/ConfigRules.py @@ -0,0 +1,249 @@ +# 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. + +from typing import Dict, List +from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set +from service.service.service_handler_api.AnyTreeTools import TreeNode + +def setup_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, + service_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + json_settings : Dict = {} if service_settings is None else service_settings.value + json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value + + service_short_uuid = service_uuid.split('-')[-1] + network_instance_name = '{:s}-NetInst'.format(service_short_uuid) + network_interface_desc = '{:s}-NetIf'.format(service_uuid) + network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) + + mtu = json_settings.get('mtu', 1450 ) # 1512 + #address_families = json_settings.get('address_families', [] ) # ['IPV4'] + bgp_as = json_settings.get('bgp_as', 0 ) # 65000 + bgp_route_target = json_settings.get('bgp_route_target', '0:0') # 65000:333 + + #router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' + route_distinguisher = json_endpoint_settings.get('route_distinguisher', '0:0' ) # '60001:801' + sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 + vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 + address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' + address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 + if_subif_name = '{:s}.{:d}'.format(endpoint_uuid, vlan_id) + + json_config_rules = [ + json_config_rule_set( + '/network_instance[{:s}]'.format(network_instance_name), { + 'name': network_instance_name, 'description': network_interface_desc, 'type': 'L3VRF', + 'route_distinguisher': route_distinguisher, + #'router_id': router_id, 'address_families': address_families, + }), + json_config_rule_set( + '/interface[{:s}]'.format(endpoint_uuid), { + 'name': endpoint_uuid, 'description': network_interface_desc, 'mtu': mtu, + }), + json_config_rule_set( + '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), { + 'name': endpoint_uuid, 'index': sub_interface_index, + 'description': network_subinterface_desc, 'vlan_id': vlan_id, + 'address_ip': address_ip, 'address_prefix': address_prefix, + }), + json_config_rule_set( + '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), { + 'name': network_instance_name, 'id': if_subif_name, 'interface': endpoint_uuid, + 'subinterface': sub_interface_index, + }), + json_config_rule_set( + '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), { + 'name': network_instance_name, 'identifier': 'BGP', 'protocol_name': 'BGP', 'as': bgp_as, + }), + json_config_rule_set( + '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { + 'name': network_instance_name, 'src_protocol': 'STATIC', 'dst_protocol': 'BGP', + 'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE', + }), + json_config_rule_set( + '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format( + network_instance_name), { + 'name': network_instance_name, 'src_protocol': 'DIRECTLY_CONNECTED', 'dst_protocol': 'BGP', + 'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE', + }), + json_config_rule_set( + '/routing_policy/bgp_defined_set[{:s}_rt_import]'.format(network_instance_name), { + 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), + }), + json_config_rule_set( + '/routing_policy/bgp_defined_set[{:s}_rt_import][route-target:{:s}]'.format( + network_instance_name, bgp_route_target), { + 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), + 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), + }), + json_config_rule_set( + '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), { + 'policy_name': '{:s}_import'.format(network_instance_name), + }), + json_config_rule_set( + '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format( + network_instance_name, '3'), { + 'policy_name': '{:s}_import'.format(network_instance_name), 'statement_name': '3', + 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), + 'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE', + }), + json_config_rule_set( + # pylint: disable=duplicate-string-formatting-argument + '/network_instance[{:s}]/inter_instance_policies[{:s}_import]'.format( + network_instance_name, network_instance_name), { + 'name': network_instance_name, 'import_policy': '{:s}_import'.format(network_instance_name), + }), + json_config_rule_set( + '/routing_policy/bgp_defined_set[{:s}_rt_export]'.format(network_instance_name), { + 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), + }), + json_config_rule_set( + '/routing_policy/bgp_defined_set[{:s}_rt_export][route-target:{:s}]'.format( + network_instance_name, bgp_route_target), { + 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), + 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), + }), + json_config_rule_set( + '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), { + 'policy_name': '{:s}_export'.format(network_instance_name), + }), + json_config_rule_set( + '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format( + network_instance_name, '3'), { + 'policy_name': '{:s}_export'.format(network_instance_name), 'statement_name': '3', + 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), + 'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE', + }), + json_config_rule_set( + # pylint: disable=duplicate-string-formatting-argument + '/network_instance[{:s}]/inter_instance_policies[{:s}_export]'.format( + network_instance_name, network_instance_name), { + 'name': network_instance_name, 'export_policy': '{:s}_export'.format(network_instance_name), + }), + ] + + return json_config_rules + +def teardown_config_rules( + service_uuid : str, connection_uuid : str, device_uuid : str, endpoint_uuid : str, + service_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + json_settings : Dict = {} if service_settings is None else service_settings.value + json_endpoint_settings : Dict = {} if endpoint_settings is None else endpoint_settings.value + + #mtu = json_settings.get('mtu', 1450 ) # 1512 + #address_families = json_settings.get('address_families', [] ) # ['IPV4'] + #bgp_as = json_settings.get('bgp_as', 0 ) # 65000 + bgp_route_target = json_settings.get('bgp_route_target', '0:0') # 65000:333 + + #router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' + #route_distinguisher = json_endpoint_settings.get('route_distinguisher', '0:0' ) # '60001:801' + sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 + vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 + #address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' + #address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 + + if_subif_name = '{:s}.{:d}'.format(endpoint_uuid, vlan_id) + service_short_uuid = service_uuid.split('-')[-1] + network_instance_name = '{:s}-NetInst'.format(service_short_uuid) + #network_interface_desc = '{:s}-NetIf'.format(service_uuid) + #network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) + + json_config_rules = [ + json_config_rule_delete( + '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), { + 'name': network_instance_name, 'id': if_subif_name, + }), + json_config_rule_delete( + '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), { + 'name': endpoint_uuid, 'index': sub_interface_index, + }), + json_config_rule_delete( + '/interface[{:s}]'.format(endpoint_uuid), { + 'name': endpoint_uuid, + }), + json_config_rule_delete( + '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format( + network_instance_name), { + 'name': network_instance_name, 'src_protocol': 'DIRECTLY_CONNECTED', 'dst_protocol': 'BGP', + 'address_family': 'IPV4', + }), + json_config_rule_delete( + '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { + 'name': network_instance_name, 'src_protocol': 'STATIC', 'dst_protocol': 'BGP', + 'address_family': 'IPV4', + }), + json_config_rule_delete( + '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), { + 'name': network_instance_name, 'identifier': 'BGP', 'protocol_name': 'BGP', + }), + json_config_rule_delete( + # pylint: disable=duplicate-string-formatting-argument + '/network_instance[{:s}]/inter_instance_policies[{:s}_import]'.format( + network_instance_name, network_instance_name), { + 'name': network_instance_name, + }), + json_config_rule_delete( + '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format( + network_instance_name, '3'), { + 'policy_name': '{:s}_import'.format(network_instance_name), 'statement_name': '3', + }), + json_config_rule_delete( + '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), { + 'policy_name': '{:s}_import'.format(network_instance_name), + }), + json_config_rule_delete( + '/routing_policy/bgp_defined_set[{:s}_rt_import][route-target:{:s}]'.format( + network_instance_name, bgp_route_target), { + 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), + 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), + }), + json_config_rule_delete( + '/routing_policy/bgp_defined_set[{:s}_rt_import]'.format(network_instance_name), { + 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), + }), + json_config_rule_delete( + # pylint: disable=duplicate-string-formatting-argument + '/network_instance[{:s}]/inter_instance_policies[{:s}_export]'.format( + network_instance_name, network_instance_name), { + 'name': network_instance_name, + }), + json_config_rule_delete( + '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format( + network_instance_name, '3'), { + 'policy_name': '{:s}_export'.format(network_instance_name), 'statement_name': '3', + }), + json_config_rule_delete( + '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), { + 'policy_name': '{:s}_export'.format(network_instance_name), + }), + json_config_rule_delete( + '/routing_policy/bgp_defined_set[{:s}_rt_export][route-target:{:s}]'.format( + network_instance_name, bgp_route_target), { + 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), + 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), + }), + json_config_rule_delete( + '/routing_policy/bgp_defined_set[{:s}_rt_export]'.format(network_instance_name), { + 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), + }), + json_config_rule_delete( + '/network_instance[{:s}]'.format(network_instance_name), { + 'name': network_instance_name + }), + ] + return json_config_rules diff --git a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py index 1faf8e7c25090a79c2d1c7a2bcec7c5db5ba8201..bdf6881647ef1a0861a312496c45512c8734afd9 100644 --- a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py @@ -13,188 +13,66 @@ # limitations under the License. import anytree, json, logging -from typing import Any, Dict, List, Optional, Tuple, Union -from common.orm.Database import Database -from common.orm.HighLevel import get_object -from common.orm.backend.Tools import key_to_str -from common.proto.context_pb2 import Device -from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set +from typing import Any, List, Optional, Tuple, Union +from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service +from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_length, chk_type -from context.client.ContextClient import ContextClient -from device.client.DeviceClient import DeviceClient -from service.service.database.ConfigModel import ORM_ConfigActionEnum, get_config_rules -from service.service.database.ContextModel import ContextModel -from service.service.database.DeviceModel import DeviceModel -from service.service.database.ServiceModel import ServiceModel from service.service.service_handler_api._ServiceHandler import _ServiceHandler from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value +from service.service.task_scheduler.TaskExecutor import TaskExecutor +from .ConfigRules import setup_config_rules, teardown_config_rules LOGGER = logging.getLogger(__name__) class L3NMOpenConfigServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called - self, db_service : ServiceModel, database : Database, context_client : ContextClient, - device_client : DeviceClient, **settings + self, service : Service, task_executor : TaskExecutor, **settings ) -> None: - self.__db_service = db_service - self.__database = database - self.__context_client = context_client # pylint: disable=unused-private-member - self.__device_client = device_client - - self.__db_context : ContextModel = get_object(self.__database, ContextModel, self.__db_service.context_fk) - str_service_key = key_to_str([self.__db_context.context_uuid, self.__db_service.service_uuid]) - db_config = get_config_rules(self.__database, str_service_key, 'running') + self.__service = service + self.__task_executor = task_executor # pylint: disable=unused-private-member self.__resolver = anytree.Resolver(pathattr='name') self.__config = TreeNode('.') - for action, resource_key, resource_value in db_config: - if action == ORM_ConfigActionEnum.SET: + for config_rule in service.service_config.config_rules: + action = config_rule.action + if config_rule.WhichOneof('config_rule') != 'custom': continue + resource_key = config_rule.custom.resource_key + resource_value = config_rule.custom.resource_value + if action == ConfigActionEnum.CONFIGACTION_SET: try: resource_value = json.loads(resource_value) except: # pylint: disable=bare-except pass set_subnode_value(self.__resolver, self.__config, resource_key, resource_value) - elif action == ORM_ConfigActionEnum.DELETE: + elif action == ConfigActionEnum.CONFIGACTION_DELETE: delete_subnode(self.__resolver, self.__config, resource_key) - def SetEndpoint(self, endpoints : List[Tuple[str, str, Optional[str]]]) -> List[Union[bool, Exception]]: + def SetEndpoint( + self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] - service_uuid = self.__db_service.service_uuid - service_short_uuid = service_uuid.split('-')[-1] - network_instance_name = '{:s}-NetInst'.format(service_short_uuid) - network_interface_desc = '{:s}-NetIf'.format(service_uuid) - network_subinterface_desc = '{:s}-NetSubIf'.format(service_uuid) - + service_uuid = self.__service.service_id.service_uuid.uuid settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None) - if settings is None: raise Exception('Unable to retrieve service settings') - json_settings : Dict = settings.value - mtu = json_settings.get('mtu', 1450 ) # 1512 - #address_families = json_settings.get('address_families', [] ) # ['IPV4'] - bgp_as = json_settings.get('bgp_as', 0 ) # 65000 - bgp_route_target = json_settings.get('bgp_route_target', '0:0') # 65000:333 results = [] for endpoint in endpoints: try: chk_type('endpoint', endpoint, (tuple, list)) chk_length('endpoint', endpoint, min_length=2, max_length=3) - if len(endpoint) == 2: - device_uuid, endpoint_uuid = endpoint - else: - device_uuid, endpoint_uuid, _ = endpoint # ignore topology_uuid by now + device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) - if endpoint_settings is None: - raise Exception('Unable to retrieve service settings for endpoint({:s})'.format( - str(endpoint_settings_uri))) - json_endpoint_settings : Dict = endpoint_settings.value - #router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' - route_distinguisher = json_endpoint_settings.get('route_distinguisher', '0:0' ) # '60001:801' - sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 - vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 - address_ip = json_endpoint_settings.get('address_ip', '0.0.0.0') # '2.2.2.1' - address_prefix = json_endpoint_settings.get('address_prefix', 24 ) # 30 - if_subif_name = '{:s}.{:d}'.format(endpoint_uuid, vlan_id) - db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True) - json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True) - json_device_config : Dict = json_device.setdefault('device_config', {}) - json_device_config_rules : List = json_device_config.setdefault('config_rules', []) - json_device_config_rules.extend([ - json_config_rule_set( - '/network_instance[{:s}]'.format(network_instance_name), { - 'name': network_instance_name, 'description': network_interface_desc, 'type': 'L3VRF', - 'route_distinguisher': route_distinguisher, - #'router_id': router_id, 'address_families': address_families, - }), - json_config_rule_set( - '/interface[{:s}]'.format(endpoint_uuid), { - 'name': endpoint_uuid, 'description': network_interface_desc, 'mtu': mtu, - }), - json_config_rule_set( - '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), { - 'name': endpoint_uuid, 'index': sub_interface_index, - 'description': network_subinterface_desc, 'vlan_id': vlan_id, - 'address_ip': address_ip, 'address_prefix': address_prefix, - }), - json_config_rule_set( - '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), { - 'name': network_instance_name, 'id': if_subif_name, 'interface': endpoint_uuid, - 'subinterface': sub_interface_index, - }), - json_config_rule_set( - '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), { - 'name': network_instance_name, 'identifier': 'BGP', 'protocol_name': 'BGP', 'as': bgp_as, - }), - json_config_rule_set( - '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { - 'name': network_instance_name, 'src_protocol': 'STATIC', 'dst_protocol': 'BGP', - 'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE', - }), - json_config_rule_set( - '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format( - network_instance_name), { - 'name': network_instance_name, 'src_protocol': 'DIRECTLY_CONNECTED', 'dst_protocol': 'BGP', - 'address_family': 'IPV4', #'default_import_policy': 'REJECT_ROUTE', - }), - json_config_rule_set( - '/routing_policy/bgp_defined_set[{:s}_rt_import]'.format(network_instance_name), { - 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), - }), - json_config_rule_set( - '/routing_policy/bgp_defined_set[{:s}_rt_import][route-target:{:s}]'.format( - network_instance_name, bgp_route_target), { - 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), - 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), - }), - json_config_rule_set( - '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), { - 'policy_name': '{:s}_import'.format(network_instance_name), - }), - json_config_rule_set( - '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format( - network_instance_name, '3'), { - 'policy_name': '{:s}_import'.format(network_instance_name), 'statement_name': '3', - 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), - 'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE', - }), - json_config_rule_set( - # pylint: disable=duplicate-string-formatting-argument - '/network_instance[{:s}]/inter_instance_policies[{:s}_import]'.format( - network_instance_name, network_instance_name), { - 'name': network_instance_name, 'import_policy': '{:s}_import'.format(network_instance_name), - }), - json_config_rule_set( - '/routing_policy/bgp_defined_set[{:s}_rt_export]'.format(network_instance_name), { - 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), - }), - json_config_rule_set( - '/routing_policy/bgp_defined_set[{:s}_rt_export][route-target:{:s}]'.format( - network_instance_name, bgp_route_target), { - 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), - 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), - }), - json_config_rule_set( - '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), { - 'policy_name': '{:s}_export'.format(network_instance_name), - }), - json_config_rule_set( - '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format( - network_instance_name, '3'), { - 'policy_name': '{:s}_export'.format(network_instance_name), 'statement_name': '3', - 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), - 'match_set_options': 'ANY', 'policy_result': 'ACCEPT_ROUTE', - }), - json_config_rule_set( - # pylint: disable=duplicate-string-formatting-argument - '/network_instance[{:s}]/inter_instance_policies[{:s}_export]'.format( - network_instance_name, network_instance_name), { - 'name': network_instance_name, 'export_policy': '{:s}_export'.format(network_instance_name), - }), - ]) - self.__device_client.ConfigureDevice(Device(**json_device)) + json_config_rules = setup_config_rules( + service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings) + + device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + del device.device_config.config_rules[:] + for json_config_rule in json_config_rules: + device.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(device) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint))) @@ -202,127 +80,33 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler): return results - def DeleteEndpoint(self, endpoints : List[Tuple[str, str, Optional[str]]]) -> List[Union[bool, Exception]]: + def DeleteEndpoint( + self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None + ) -> List[Union[bool, Exception]]: chk_type('endpoints', endpoints, list) if len(endpoints) == 0: return [] - service_uuid = self.__db_service.service_uuid - service_short_uuid = service_uuid.split('-')[-1] - network_instance_name = '{:s}-NetInst'.format(service_short_uuid) - + service_uuid = self.__service.service_id.service_uuid.uuid settings : TreeNode = get_subnode(self.__resolver, self.__config, '/settings', None) - if settings is None: raise Exception('Unable to retrieve service settings') - json_settings : Dict = settings.value - bgp_route_target = json_settings.get('bgp_route_target', '0:0') # 65000:333 results = [] for endpoint in endpoints: try: chk_type('endpoint', endpoint, (tuple, list)) chk_length('endpoint', endpoint, min_length=2, max_length=3) - if len(endpoint) == 2: - device_uuid, endpoint_uuid = endpoint - else: - device_uuid, endpoint_uuid, _ = endpoint # ignore topology_uuid by now + device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now endpoint_settings_uri = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) - if endpoint_settings is None: - raise Exception('Unable to retrieve service settings for endpoint({:s})'.format( - str(endpoint_settings_uri))) - json_endpoint_settings : Dict = endpoint_settings.value - sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 - vlan_id = json_endpoint_settings.get('vlan_id', 1 ) # 400 - if_subif_name = '{:s}.{:d}'.format(endpoint_uuid, vlan_id) - db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True) - json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True) - json_device_config : Dict = json_device.setdefault('device_config', {}) - json_device_config_rules : List = json_device_config.setdefault('config_rules', []) - json_device_config_rules.extend([ - json_config_rule_delete( - '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, if_subif_name), { - 'name': network_instance_name, 'id': if_subif_name, - }), - json_config_rule_delete( - '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), { - 'name': endpoint_uuid, 'index': sub_interface_index, - }), - json_config_rule_delete( - '/interface[{:s}]'.format(endpoint_uuid), { - 'name': endpoint_uuid, - }), - json_config_rule_delete( - '/network_instance[{:s}]/table_connections[DIRECTLY_CONNECTED][BGP][IPV4]'.format( - network_instance_name), { - 'name': network_instance_name, 'src_protocol': 'DIRECTLY_CONNECTED', 'dst_protocol': 'BGP', - 'address_family': 'IPV4', - }), - json_config_rule_delete( - '/network_instance[{:s}]/table_connections[STATIC][BGP][IPV4]'.format(network_instance_name), { - 'name': network_instance_name, 'src_protocol': 'STATIC', 'dst_protocol': 'BGP', - 'address_family': 'IPV4', - }), - json_config_rule_delete( - '/network_instance[{:s}]/protocols[BGP]'.format(network_instance_name), { - 'name': network_instance_name, 'identifier': 'BGP', 'protocol_name': 'BGP', - }), - json_config_rule_delete( - # pylint: disable=duplicate-string-formatting-argument - '/network_instance[{:s}]/inter_instance_policies[{:s}_import]'.format( - network_instance_name, network_instance_name), { - 'name': network_instance_name, - }), - json_config_rule_delete( - '/routing_policy/policy_definition[{:s}_import]/statement[{:s}]'.format( - network_instance_name, '3'), { - 'policy_name': '{:s}_import'.format(network_instance_name), 'statement_name': '3', - }), - json_config_rule_delete( - '/routing_policy/policy_definition[{:s}_import]'.format(network_instance_name), { - 'policy_name': '{:s}_import'.format(network_instance_name), - }), - json_config_rule_delete( - '/routing_policy/bgp_defined_set[{:s}_rt_import][route-target:{:s}]'.format( - network_instance_name, bgp_route_target), { - 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), - 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), - }), - json_config_rule_delete( - '/routing_policy/bgp_defined_set[{:s}_rt_import]'.format(network_instance_name), { - 'ext_community_set_name': '{:s}_rt_import'.format(network_instance_name), - }), - json_config_rule_delete( - # pylint: disable=duplicate-string-formatting-argument - '/network_instance[{:s}]/inter_instance_policies[{:s}_export]'.format( - network_instance_name, network_instance_name), { - 'name': network_instance_name, - }), - json_config_rule_delete( - '/routing_policy/policy_definition[{:s}_export]/statement[{:s}]'.format( - network_instance_name, '3'), { - 'policy_name': '{:s}_export'.format(network_instance_name), 'statement_name': '3', - }), - json_config_rule_delete( - '/routing_policy/policy_definition[{:s}_export]'.format(network_instance_name), { - 'policy_name': '{:s}_export'.format(network_instance_name), - }), - json_config_rule_delete( - '/routing_policy/bgp_defined_set[{:s}_rt_export][route-target:{:s}]'.format( - network_instance_name, bgp_route_target), { - 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), - 'ext_community_member' : 'route-target:{:s}'.format(bgp_route_target), - }), - json_config_rule_delete( - '/routing_policy/bgp_defined_set[{:s}_rt_export]'.format(network_instance_name), { - 'ext_community_set_name': '{:s}_rt_export'.format(network_instance_name), - }), - json_config_rule_delete( - '/network_instance[{:s}]'.format(network_instance_name), { - 'name': network_instance_name - }), - ]) - self.__device_client.ConfigureDevice(Device(**json_device)) + json_config_rules = teardown_config_rules( + service_uuid, connection_uuid, device_uuid, endpoint_uuid, settings, endpoint_settings) + + device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + del device.device_config.config_rules[:] + for json_config_rule in json_config_rules: + device.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(device) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint)))