Loading src/context/service/database/models/enums/ServiceType.py +1 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ class ORM_ServiceTypeEnum(enum.Enum): TE = ServiceTypeEnum.SERVICETYPE_TE E2E = ServiceTypeEnum.SERVICETYPE_E2E OPTICAL_CONNECTIVITY = ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY QKD = ServiceTypeEnum.SERVICETYPE_QKD grpc_to_enum__service_type = functools.partial( grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum) src/service/service/service_handler_api/FilterFields.py +4 −2 Original line number Diff line number Diff line Loading @@ -26,7 +26,8 @@ SERVICE_TYPE_VALUES = { ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, ServiceTypeEnum.SERVICETYPE_TE, ServiceTypeEnum.SERVICETYPE_E2E, ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY, ServiceTypeEnum.SERVICETYPE_QKD, } DEVICE_DRIVER_VALUES = { Loading @@ -41,7 +42,8 @@ DEVICE_DRIVER_VALUES = { DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_OPTICAL_TFS, DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN, DeviceDriverEnum.DEVICEDRIVER_OC DeviceDriverEnum.DEVICEDRIVER_OC, DeviceDriverEnum.DEVICEDRIVER_QKD, } # Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified Loading src/service/service/service_handlers/__init__.py +7 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ from .tapi_tapi.TapiServiceHandler import TapiServiceHandler from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler from .e2e_orch.E2EOrchestratorServiceHandler import E2EOrchestratorServiceHandler from .oc.OCServiceHandler import OCServiceHandler from .qkd.qkd_service_handler import QKDServiceHandler SERVICE_HANDLERS = [ (L2NMEmulatedServiceHandler, [ Loading Loading @@ -106,5 +107,11 @@ SERVICE_HANDLERS = [ FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY, FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC, } ]), (QKDServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_QKD, FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_QKD], } ]) ] src/service/service/service_handlers/qkd/__init__.py 0 → 100644 +14 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. src/service/service/service_handlers/qkd/qkd_service_handler.py 0 → 100644 +394 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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, logging, uuid from typing import Any, Dict, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.proto.context_pb2 import ConfigRule, DeviceId, Service from common.proto.app_pb2 import App, QKDAppStatusEnum, QKDAppTypesEnum from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_type from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching from service.service.service_handler_api._ServiceHandler import _ServiceHandler from service.service.service_handler_api.SettingsHandler import SettingsHandler from service.service.task_scheduler.TaskExecutor import TaskExecutor LOGGER = logging.getLogger(__name__) def get_endpoint_name_by_uuid(device, uuid): for device_endpoint in device.device_endpoints: if device_endpoint.endpoint_id.endpoint_uuid.uuid == uuid: return device_endpoint.name return None class QKDServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings ) -> None: self.__service = service self.__task_executor = task_executor self.__settings_handler = SettingsHandler(service.service_config, **settings) # Optare: This function is where the service is created # Optare: It already receives the path provided by pathcomp in endpoints variable # Optare: It then checks for each respective QKD Node and requests SBI to inform of the new connection # Optare: It also requests app module for a creation of internal service if the service is virtual 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) < 2 or len(endpoints) % 2: return [] LOGGER.info('Endpoints: ' + str(endpoints)) service_uuid = self.__service.service_id.service_uuid.uuid settings = self.__settings_handler.get('/settings') context_uuid = self.__service.service_id.context_id.context_uuid.uuid results = [] try: if len(endpoints) > 4: is_virtual = True else: is_virtual = False devices = [] qkdn_ids = [] interfaces = [] links = [] # Optare: First a big iteration through all devices is done in order to obtain all information needed for the whole operation # Optare: This is a way to minimize time of operation. Otherwise it would require many O(N) operations. This way we can reduce it to one # Populate devices and QKDN ids for idx, endpoint in enumerate(endpoints[::2]): device_uuid, endpoint_left_uuid = get_device_endpoint_uuids(endpoint) _, endpoint_right_uuid = get_device_endpoint_uuids(endpoints[2 * idx + 1]) device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) devices.append(device) interfaces.append([0,0]) links.append([]) endpoint_left = get_endpoint_name_by_uuid(device, endpoint_left_uuid) if idx > 0 else None endpoint_right = get_endpoint_name_by_uuid(device, endpoint_right_uuid) if 2 * idx + 2 < len(endpoints) else None for config_rule in device.device_config.config_rules: resource_key = config_rule.custom.resource_key if resource_key == '__node__': value = json.loads(config_rule.custom.resource_value) qkdn_ids.append(value['qkdn_id']) elif resource_key.startswith('/interface'): value = json.loads(config_rule.custom.resource_value) try: endpoint_str = value['qkdi_att_point']['uuid'] LOGGER.info("A: " + str(endpoint_str) + "....." + str(endpoint_left) + "....." + str(endpoint_right)) if endpoint_str == endpoint_left: interfaces[idx][0] = value['qkdi_id'] elif endpoint_str == endpoint_right: interfaces[idx][1] = value['qkdi_id'] except KeyError: pass elif resource_key.startswith('/link'): value = json.loads(config_rule.custom.resource_value) links[idx].append(( value['uuid'], (value['src_qkdn_id'], value['src_interface_id']), (value['dst_qkdn_id'], value['dst_interface_id']) )) LOGGER.info("IFs: " + str(interfaces)) LOGGER.info("Links: " + str(links)) LOGGER.info("context_: " + context_uuid) # Optare: From here now is where the work is really done. It iterates over every device in use for the service (ordered) src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0]) src_device = devices[0] src_endpoint = get_endpoint_matching(src_device, src_endpoint_uuid) dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(endpoints[-1]) dst_device = devices[-1] dst_endpoint = get_endpoint_matching(dst_device, dst_endpoint_uuid) src_qkdn_id = qkdn_ids[0] dst_qkdn_id = qkdn_ids[-1] src_interface_id = interfaces[0][1] dst_interface_id = interfaces[-1][0] service_qkdl_id_src_dst = str(uuid.uuid4()) service_qkdl_id_dst_src = str(uuid.uuid4()) for idx, device in enumerate(devices): # Even though we always create them together. There is a chance the admin deletes one of the rules manually phys_qkdl_id_right = None if idx == (len(devices) - 1) else '' # None == impossible phys_qkdl_id_left = None if idx == 0 else '' # None == impossible for link_uuid, link_src, link_dst in links[idx]: qkdn_link_src, qkdn_interface_src = link_src qkdn_link_dst, qkdn_interface_dst = link_dst if phys_qkdl_id_right == '' and \ qkdn_link_src == qkdn_ids[idx] and qkdn_interface_src == interfaces[idx][1] and \ qkdn_link_dst[idx+1] and qkdn_interface_dst == interfaces[idx+1][0]: phys_qkdl_id_right = link_uuid if phys_qkdl_id_left == '' and \ qkdn_link_src == qkdn_ids[idx] and qkdn_interface_src == interfaces[idx][0] and \ qkdn_link_dst[idx-1] and qkdn_interface_dst == interfaces[idx-1][1]: phys_qkdl_id_left = link_uuid # Optare: Before adding information to config_rules you have to delete the list first otherwise old content will be called again del device.device_config.config_rules[:] if phys_qkdl_id_right: if not is_virtual: service_qkdl_id_src_dst = phys_qkdl_id_right elif phys_qkdl_id_right == '': qkdl_id_src_dst = str(uuid.uuid4()) if is_virtual else service_qkdl_id_src_dst json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(qkdl_id_src_dst), { 'uuid' : qkdl_id_src_dst, 'type' : 'DIRECT', 'src_qkdn_id' : qkdn_ids[idx], 'src_interface_id' : interfaces[idx][1], 'dst_qkdn_id' : qkdn_ids[idx+1], 'dst_interface_id' : interfaces[idx+1][0], }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) if phys_qkdl_id_left: if not is_virtual: service_qkdl_id_dst_src = phys_qkdl_id_left elif phys_qkdl_id_left == '': qkdl_id_dst_src = str(uuid.uuid4()) if is_virtual else service_qkdl_id_dst_src json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(qkdl_id_dst_src), { 'uuid' : qkdl_id_dst_src, 'type' : 'DIRECT', 'src_qkdn_id' : qkdn_ids[idx], 'src_interface_id' : interfaces[idx][0], 'dst_qkdn_id' : qkdn_ids[idx-1], 'dst_interface_id' : interfaces[idx-1][1], }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) if is_virtual: if idx < len(qkdn_ids) - 1: json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(service_qkdl_id_src_dst), { 'uuid' : service_qkdl_id_src_dst, 'type' : 'VIRTUAL', 'src_qkdn_id' : src_qkdn_id, 'src_interface_id' : src_interface_id, 'dst_qkdn_id' : dst_qkdn_id, 'dst_interface_id' : dst_interface_id, 'virt_prev_hop' : qkdn_ids[idx-1] if idx > 0 else None, 'virt_next_hops' : qkdn_ids[idx+1:], 'virt_bandwidth' : 0, }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) if idx > 0: json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(service_qkdl_id_dst_src), { 'uuid' : service_qkdl_id_dst_src, 'type' : 'VIRTUAL', 'src_qkdn_id' : dst_qkdn_id, 'src_interface_id' : dst_interface_id, 'dst_qkdn_id' : src_qkdn_id, 'dst_interface_id' : src_interface_id, 'virt_prev_hop' : qkdn_ids[idx+1] if idx < len(qkdn_ids) - 1 else None, 'virt_next_hops' : qkdn_ids[idx-1::-1], 'virt_bandwidth' : 0, }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), { 'uuid' : service_uuid, 'qkdl_id_src_dst' : service_qkdl_id_src_dst, 'qkdl_id_dst_src' : service_qkdl_id_dst_src, }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) self.__task_executor.configure_device(device) if is_virtual: # Register App internal_app_src_dst = { 'app_id': {'context_id': {'context_uuid': {'uuid': context_uuid}}, 'app_uuid': {'uuid': str(uuid.uuid4())}}, 'app_status': QKDAppStatusEnum.QKDAPPSTATUS_ON, 'app_type': QKDAppTypesEnum.QKDAPPTYPES_INTERNAL, 'server_app_id': '', 'client_app_id': [], 'backing_qkdl_id': [{'qkdl_uuid': {'uuid': service_qkdl_id_src_dst}}], 'local_device_id': src_device.device_id, 'remote_device_id': dst_device.device_id, } self.__task_executor.register_app(App(**internal_app_src_dst)) # Register App internal_app_dst_src = { 'app_id': {'context_id': {'context_uuid': {'uuid': context_uuid}}, 'app_uuid': {'uuid': str(uuid.uuid4())}}, 'app_status': QKDAppStatusEnum.QKDAPPSTATUS_ON, 'app_type': QKDAppTypesEnum.QKDAPPTYPES_INTERNAL, 'server_app_id': '', 'client_app_id': [], 'backing_qkdl_id': [{'qkdl_uuid': {'uuid': service_qkdl_id_dst_src}}], 'local_device_id': dst_device.device_id, 'remote_device_id': src_device.device_id, } self.__task_executor.register_app(App(**internal_app_dst_src)) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid))) results.append(e) return results # Optare: This will be to delete a service def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: """ Delete service endpoints form a list. Parameters: endpoints: List[Tuple[str, str, Optional[str]]] List of tuples, each containing a device_uuid, endpoint_uuid, and the topology_uuid of the endpoint to be removed. connection_uuid : Optional[str] If specified, is the UUID of the connection this endpoint is associated to. Returns: results: List[Union[bool, Exception]] List of results for endpoint deletions requested. Return values must be in the same order as the requested endpoints. If an endpoint is properly deleted, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError() # Optare: Can be ingored. It's in case if a service is later updated. Not required to proper functioning def SetConstraint(self, constraints: List[Tuple[str, Any]]) \ -> List[Union[bool, Exception]]: """ Create/Update service constraints. Parameters: constraints: List[Tuple[str, Any]] List of tuples, each containing a constraint_type and the new constraint_value to be set. Returns: results: List[Union[bool, Exception]] List of results for constraint changes requested. Return values must be in the same order as the requested constraints. If a constraint is properly set, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError() def DeleteConstraint(self, constraints: List[Tuple[str, Any]]) \ -> List[Union[bool, Exception]]: """ Delete service constraints. Parameters: constraints: List[Tuple[str, Any]] List of tuples, each containing a constraint_type pointing to the constraint to be deleted, and a constraint_value containing possible additionally required values to locate the constraint to be removed. Returns: results: List[Union[bool, Exception]] List of results for constraint deletions requested. Return values must be in the same order as the requested constraints. If a constraint is properly deleted, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError() def SetConfig(self, resources: List[Tuple[str, Any]]) \ -> List[Union[bool, Exception]]: """ Create/Update configuration for a list of service resources. Parameters: resources: List[Tuple[str, Any]] List of tuples, each containing a resource_key pointing to the resource to be modified, and a resource_value containing the new value to be set. Returns: results: List[Union[bool, Exception]] List of results for resource key changes requested. Return values must be in the same order as the requested resource keys. If a resource is properly set, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError() def DeleteConfig(self, resources: List[Tuple[str, Any]]) \ -> List[Union[bool, Exception]]: """ Delete configuration for a list of service resources. Parameters: resources: List[Tuple[str, Any]] List of tuples, each containing a resource_key pointing to the resource to be modified, and a resource_value containing possible additionally required values to locate the value to be removed. Returns: results: List[Union[bool, Exception]] List of results for resource key deletions requested. Return values must be in the same order as the requested resource keys. If a resource is properly deleted, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError() Loading
src/context/service/database/models/enums/ServiceType.py +1 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ class ORM_ServiceTypeEnum(enum.Enum): TE = ServiceTypeEnum.SERVICETYPE_TE E2E = ServiceTypeEnum.SERVICETYPE_E2E OPTICAL_CONNECTIVITY = ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY QKD = ServiceTypeEnum.SERVICETYPE_QKD grpc_to_enum__service_type = functools.partial( grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum)
src/service/service/service_handler_api/FilterFields.py +4 −2 Original line number Diff line number Diff line Loading @@ -26,7 +26,8 @@ SERVICE_TYPE_VALUES = { ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, ServiceTypeEnum.SERVICETYPE_TE, ServiceTypeEnum.SERVICETYPE_E2E, ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY, ServiceTypeEnum.SERVICETYPE_QKD, } DEVICE_DRIVER_VALUES = { Loading @@ -41,7 +42,8 @@ DEVICE_DRIVER_VALUES = { DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_OPTICAL_TFS, DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN, DeviceDriverEnum.DEVICEDRIVER_OC DeviceDriverEnum.DEVICEDRIVER_OC, DeviceDriverEnum.DEVICEDRIVER_QKD, } # Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified Loading
src/service/service/service_handlers/__init__.py +7 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ from .tapi_tapi.TapiServiceHandler import TapiServiceHandler from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler from .e2e_orch.E2EOrchestratorServiceHandler import E2EOrchestratorServiceHandler from .oc.OCServiceHandler import OCServiceHandler from .qkd.qkd_service_handler import QKDServiceHandler SERVICE_HANDLERS = [ (L2NMEmulatedServiceHandler, [ Loading Loading @@ -106,5 +107,11 @@ SERVICE_HANDLERS = [ FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY, FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC, } ]), (QKDServiceHandler, [ { FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_QKD, FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_QKD], } ]) ]
src/service/service/service_handlers/qkd/__init__.py 0 → 100644 +14 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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.
src/service/service/service_handlers/qkd/qkd_service_handler.py 0 → 100644 +394 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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, logging, uuid from typing import Any, Dict, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.proto.context_pb2 import ConfigRule, DeviceId, Service from common.proto.app_pb2 import App, QKDAppStatusEnum, QKDAppTypesEnum from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_type from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching from service.service.service_handler_api._ServiceHandler import _ServiceHandler from service.service.service_handler_api.SettingsHandler import SettingsHandler from service.service.task_scheduler.TaskExecutor import TaskExecutor LOGGER = logging.getLogger(__name__) def get_endpoint_name_by_uuid(device, uuid): for device_endpoint in device.device_endpoints: if device_endpoint.endpoint_id.endpoint_uuid.uuid == uuid: return device_endpoint.name return None class QKDServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings ) -> None: self.__service = service self.__task_executor = task_executor self.__settings_handler = SettingsHandler(service.service_config, **settings) # Optare: This function is where the service is created # Optare: It already receives the path provided by pathcomp in endpoints variable # Optare: It then checks for each respective QKD Node and requests SBI to inform of the new connection # Optare: It also requests app module for a creation of internal service if the service is virtual 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) < 2 or len(endpoints) % 2: return [] LOGGER.info('Endpoints: ' + str(endpoints)) service_uuid = self.__service.service_id.service_uuid.uuid settings = self.__settings_handler.get('/settings') context_uuid = self.__service.service_id.context_id.context_uuid.uuid results = [] try: if len(endpoints) > 4: is_virtual = True else: is_virtual = False devices = [] qkdn_ids = [] interfaces = [] links = [] # Optare: First a big iteration through all devices is done in order to obtain all information needed for the whole operation # Optare: This is a way to minimize time of operation. Otherwise it would require many O(N) operations. This way we can reduce it to one # Populate devices and QKDN ids for idx, endpoint in enumerate(endpoints[::2]): device_uuid, endpoint_left_uuid = get_device_endpoint_uuids(endpoint) _, endpoint_right_uuid = get_device_endpoint_uuids(endpoints[2 * idx + 1]) device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) devices.append(device) interfaces.append([0,0]) links.append([]) endpoint_left = get_endpoint_name_by_uuid(device, endpoint_left_uuid) if idx > 0 else None endpoint_right = get_endpoint_name_by_uuid(device, endpoint_right_uuid) if 2 * idx + 2 < len(endpoints) else None for config_rule in device.device_config.config_rules: resource_key = config_rule.custom.resource_key if resource_key == '__node__': value = json.loads(config_rule.custom.resource_value) qkdn_ids.append(value['qkdn_id']) elif resource_key.startswith('/interface'): value = json.loads(config_rule.custom.resource_value) try: endpoint_str = value['qkdi_att_point']['uuid'] LOGGER.info("A: " + str(endpoint_str) + "....." + str(endpoint_left) + "....." + str(endpoint_right)) if endpoint_str == endpoint_left: interfaces[idx][0] = value['qkdi_id'] elif endpoint_str == endpoint_right: interfaces[idx][1] = value['qkdi_id'] except KeyError: pass elif resource_key.startswith('/link'): value = json.loads(config_rule.custom.resource_value) links[idx].append(( value['uuid'], (value['src_qkdn_id'], value['src_interface_id']), (value['dst_qkdn_id'], value['dst_interface_id']) )) LOGGER.info("IFs: " + str(interfaces)) LOGGER.info("Links: " + str(links)) LOGGER.info("context_: " + context_uuid) # Optare: From here now is where the work is really done. It iterates over every device in use for the service (ordered) src_device_uuid, src_endpoint_uuid = get_device_endpoint_uuids(endpoints[0]) src_device = devices[0] src_endpoint = get_endpoint_matching(src_device, src_endpoint_uuid) dst_device_uuid, dst_endpoint_uuid = get_device_endpoint_uuids(endpoints[-1]) dst_device = devices[-1] dst_endpoint = get_endpoint_matching(dst_device, dst_endpoint_uuid) src_qkdn_id = qkdn_ids[0] dst_qkdn_id = qkdn_ids[-1] src_interface_id = interfaces[0][1] dst_interface_id = interfaces[-1][0] service_qkdl_id_src_dst = str(uuid.uuid4()) service_qkdl_id_dst_src = str(uuid.uuid4()) for idx, device in enumerate(devices): # Even though we always create them together. There is a chance the admin deletes one of the rules manually phys_qkdl_id_right = None if idx == (len(devices) - 1) else '' # None == impossible phys_qkdl_id_left = None if idx == 0 else '' # None == impossible for link_uuid, link_src, link_dst in links[idx]: qkdn_link_src, qkdn_interface_src = link_src qkdn_link_dst, qkdn_interface_dst = link_dst if phys_qkdl_id_right == '' and \ qkdn_link_src == qkdn_ids[idx] and qkdn_interface_src == interfaces[idx][1] and \ qkdn_link_dst[idx+1] and qkdn_interface_dst == interfaces[idx+1][0]: phys_qkdl_id_right = link_uuid if phys_qkdl_id_left == '' and \ qkdn_link_src == qkdn_ids[idx] and qkdn_interface_src == interfaces[idx][0] and \ qkdn_link_dst[idx-1] and qkdn_interface_dst == interfaces[idx-1][1]: phys_qkdl_id_left = link_uuid # Optare: Before adding information to config_rules you have to delete the list first otherwise old content will be called again del device.device_config.config_rules[:] if phys_qkdl_id_right: if not is_virtual: service_qkdl_id_src_dst = phys_qkdl_id_right elif phys_qkdl_id_right == '': qkdl_id_src_dst = str(uuid.uuid4()) if is_virtual else service_qkdl_id_src_dst json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(qkdl_id_src_dst), { 'uuid' : qkdl_id_src_dst, 'type' : 'DIRECT', 'src_qkdn_id' : qkdn_ids[idx], 'src_interface_id' : interfaces[idx][1], 'dst_qkdn_id' : qkdn_ids[idx+1], 'dst_interface_id' : interfaces[idx+1][0], }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) if phys_qkdl_id_left: if not is_virtual: service_qkdl_id_dst_src = phys_qkdl_id_left elif phys_qkdl_id_left == '': qkdl_id_dst_src = str(uuid.uuid4()) if is_virtual else service_qkdl_id_dst_src json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(qkdl_id_dst_src), { 'uuid' : qkdl_id_dst_src, 'type' : 'DIRECT', 'src_qkdn_id' : qkdn_ids[idx], 'src_interface_id' : interfaces[idx][0], 'dst_qkdn_id' : qkdn_ids[idx-1], 'dst_interface_id' : interfaces[idx-1][1], }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) if is_virtual: if idx < len(qkdn_ids) - 1: json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(service_qkdl_id_src_dst), { 'uuid' : service_qkdl_id_src_dst, 'type' : 'VIRTUAL', 'src_qkdn_id' : src_qkdn_id, 'src_interface_id' : src_interface_id, 'dst_qkdn_id' : dst_qkdn_id, 'dst_interface_id' : dst_interface_id, 'virt_prev_hop' : qkdn_ids[idx-1] if idx > 0 else None, 'virt_next_hops' : qkdn_ids[idx+1:], 'virt_bandwidth' : 0, }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) if idx > 0: json_config_rule = json_config_rule_set('/link/link[{:s}]'.format(service_qkdl_id_dst_src), { 'uuid' : service_qkdl_id_dst_src, 'type' : 'VIRTUAL', 'src_qkdn_id' : dst_qkdn_id, 'src_interface_id' : dst_interface_id, 'dst_qkdn_id' : src_qkdn_id, 'dst_interface_id' : src_interface_id, 'virt_prev_hop' : qkdn_ids[idx+1] if idx < len(qkdn_ids) - 1 else None, 'virt_next_hops' : qkdn_ids[idx-1::-1], 'virt_bandwidth' : 0, }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), { 'uuid' : service_uuid, 'qkdl_id_src_dst' : service_qkdl_id_src_dst, 'qkdl_id_dst_src' : service_qkdl_id_dst_src, }) device.device_config.config_rules.append(ConfigRule(**json_config_rule)) self.__task_executor.configure_device(device) if is_virtual: # Register App internal_app_src_dst = { 'app_id': {'context_id': {'context_uuid': {'uuid': context_uuid}}, 'app_uuid': {'uuid': str(uuid.uuid4())}}, 'app_status': QKDAppStatusEnum.QKDAPPSTATUS_ON, 'app_type': QKDAppTypesEnum.QKDAPPTYPES_INTERNAL, 'server_app_id': '', 'client_app_id': [], 'backing_qkdl_id': [{'qkdl_uuid': {'uuid': service_qkdl_id_src_dst}}], 'local_device_id': src_device.device_id, 'remote_device_id': dst_device.device_id, } self.__task_executor.register_app(App(**internal_app_src_dst)) # Register App internal_app_dst_src = { 'app_id': {'context_id': {'context_uuid': {'uuid': context_uuid}}, 'app_uuid': {'uuid': str(uuid.uuid4())}}, 'app_status': QKDAppStatusEnum.QKDAPPSTATUS_ON, 'app_type': QKDAppTypesEnum.QKDAPPTYPES_INTERNAL, 'server_app_id': '', 'client_app_id': [], 'backing_qkdl_id': [{'qkdl_uuid': {'uuid': service_qkdl_id_dst_src}}], 'local_device_id': dst_device.device_id, 'remote_device_id': src_device.device_id, } self.__task_executor.register_app(App(**internal_app_dst_src)) results.append(True) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Unable to SetEndpoint for Service({:s})'.format(str(service_uuid))) results.append(e) return results # Optare: This will be to delete a service def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: """ Delete service endpoints form a list. Parameters: endpoints: List[Tuple[str, str, Optional[str]]] List of tuples, each containing a device_uuid, endpoint_uuid, and the topology_uuid of the endpoint to be removed. connection_uuid : Optional[str] If specified, is the UUID of the connection this endpoint is associated to. Returns: results: List[Union[bool, Exception]] List of results for endpoint deletions requested. Return values must be in the same order as the requested endpoints. If an endpoint is properly deleted, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError() # Optare: Can be ingored. It's in case if a service is later updated. Not required to proper functioning def SetConstraint(self, constraints: List[Tuple[str, Any]]) \ -> List[Union[bool, Exception]]: """ Create/Update service constraints. Parameters: constraints: List[Tuple[str, Any]] List of tuples, each containing a constraint_type and the new constraint_value to be set. Returns: results: List[Union[bool, Exception]] List of results for constraint changes requested. Return values must be in the same order as the requested constraints. If a constraint is properly set, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError() def DeleteConstraint(self, constraints: List[Tuple[str, Any]]) \ -> List[Union[bool, Exception]]: """ Delete service constraints. Parameters: constraints: List[Tuple[str, Any]] List of tuples, each containing a constraint_type pointing to the constraint to be deleted, and a constraint_value containing possible additionally required values to locate the constraint to be removed. Returns: results: List[Union[bool, Exception]] List of results for constraint deletions requested. Return values must be in the same order as the requested constraints. If a constraint is properly deleted, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError() def SetConfig(self, resources: List[Tuple[str, Any]]) \ -> List[Union[bool, Exception]]: """ Create/Update configuration for a list of service resources. Parameters: resources: List[Tuple[str, Any]] List of tuples, each containing a resource_key pointing to the resource to be modified, and a resource_value containing the new value to be set. Returns: results: List[Union[bool, Exception]] List of results for resource key changes requested. Return values must be in the same order as the requested resource keys. If a resource is properly set, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError() def DeleteConfig(self, resources: List[Tuple[str, Any]]) \ -> List[Union[bool, Exception]]: """ Delete configuration for a list of service resources. Parameters: resources: List[Tuple[str, Any]] List of tuples, each containing a resource_key pointing to the resource to be modified, and a resource_value containing possible additionally required values to locate the value to be removed. Returns: results: List[Union[bool, Exception]] List of results for resource key deletions requested. Return values must be in the same order as the requested resource keys. If a resource is properly deleted, True must be returned; otherwise, the Exception that is raised during the processing must be returned. """ raise NotImplementedError()