diff --git a/deploy/tfs.sh b/deploy/tfs.sh index 3a01606ce32369d0f0968282569eaf4ed59d778c..19c0d75a095bc5dc239d1d95237378bcf2fbb8da 100755 --- a/deploy/tfs.sh +++ b/deploy/tfs.sh @@ -364,7 +364,7 @@ for COMPONENT in $TFS_COMPONENTS; do echo "Waiting for '$COMPONENT' component..." COMPONENT_OBJNAME=$(echo "${COMPONENT}" | sed "s/\_/-/") kubectl wait --namespace $TFS_K8S_NAMESPACE \ - --for='condition=available' --timeout=300s deployment/${COMPONENT_OBJNAME}service + --for='condition=available' --timeout=90s deployment/${COMPONENT_OBJNAME}service printf "\n" done diff --git a/manifests/opticalcontrollerservice.yaml b/manifests/opticalcontrollerservice.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4b677ee4f7287b0790aaa0b19f034db03978fac0 --- /dev/null +++ b/manifests/opticalcontrollerservice.yaml @@ -0,0 +1,72 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: opticalcontrollerservice +spec: + selector: + matchLabels: + app: opticalcontrollerservice + replicas: 1 + template: + metadata: + labels: + app: opticalcontrollerservice + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: localhost:32000/tfs/opticalcontroller:dev + imagePullPolicy: Never + ports: + - containerPort: 10060 + - containerPort: 9192 + env: + - name: LOG_LEVEL + value: "INFO" + #readinessProbe: + # exec: + # command: ["/bin/grpc_health_probe", "-addr=:10060"] + #livenessProbe: + # exec: + # command: ["/bin/grpc_health_probe", "-addr=:10060"] + resources: + requests: + cpu: 500m + memory: 128Mi + limits: + cpu: 1000m + memory: 1024Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: opticalcontrollerservice + labels: + app: opticalcontrollerservice +spec: + type: ClusterIP + selector: + app: opticalcontrollerservice + ports: + - name: grpc + protocol: TCP + port: 10060 + targetPort: 10060 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 diff --git a/my_deploy.sh b/my_deploy.sh index 0c5ba03c5b9f63038a622f44ab2dfaaf1e7e6ada..7dd5e5c3ee13cbce2b701b5b4e703823dfb2c28f 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -25,9 +25,12 @@ export TFS_COMPONENTS="context device pathcomp service slice nbi webui load_gene # Uncomment to activate Monitoring #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" -# Uncomment to activate bgpls_speaker +# Uncomment to activate BGP-LS Speaker #export TFS_COMPONENTS="${TFS_COMPONENTS} bgpls_speaker" +# Uncomment to activate Optical Controller +#export TFS_COMPONENTS="${TFS_COMPONENTS} opticalcontroller" + # Uncomment to activate ZTP #export TFS_COMPONENTS="${TFS_COMPONENTS} ztp" diff --git a/ofc24 b/ofc24 new file mode 120000 index 0000000000000000000000000000000000000000..baae92c8e249f4abfbbcf9739c9162087fefbacd --- /dev/null +++ b/ofc24 @@ -0,0 +1 @@ +src/tests/ofc24/ \ No newline at end of file diff --git a/proto/context.proto b/proto/context.proto index d5022ac292f04cd2e9b80f690be3077e7aedd868..8a6b019dc99863da32631425d954afbaf167f1b7 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -74,6 +74,16 @@ service ContextService { rpc SetConnection (Connection ) returns ( ConnectionId ) {} rpc RemoveConnection (ConnectionId ) returns ( Empty ) {} rpc GetConnectionEvents(Empty ) returns (stream ConnectionEvent ) {} + + + // ------------------------------ Experimental ----------------------------- + rpc GetOpticalConfig (Empty ) returns (OpticalConfigList ) {} + rpc SetOpticalConfig (OpticalConfig ) returns (OpticalConfigId ) {} + rpc SelectOpticalConfig(OpticalConfigId) returns (OpticalConfig ) {} + + rpc SetOpticalLink (OpticalLink ) returns (Empty ) {} + rpc GetOpticalLink (OpticalLinkId ) returns (OpticalLink ) {} + rpc GetFiber (FiberId ) returns (Fiber ) {} } // ----- Generic ------------------------------------------------------------------------------------------------------- @@ -203,6 +213,7 @@ enum DeviceDriverEnum { DEVICEDRIVER_GNMI_OPENCONFIG = 8; DEVICEDRIVER_FLEXSCALE = 9; DEVICEDRIVER_IETF_ACTN = 10; + DEVICEDRIVER_OC = 11; } enum DeviceOperationalStatusEnum { @@ -288,6 +299,7 @@ enum ServiceTypeEnum { SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3; SERVICETYPE_TE = 4; SERVICETYPE_E2E = 5; + SERVICETYPE_OPTICAL_CONNECTIVITY = 6; } enum ServiceStatusEnum { @@ -612,3 +624,53 @@ message AuthenticationResult { ContextId context_id = 1; bool authenticated = 2; } + +// ---------------- Experimental ------------------------ +message OpticalConfigId { + string opticalconfig_uuid = 1; +} +message OpticalConfig { + OpticalConfigId opticalconfig_id = 1; + string config = 2; +} + +message OpticalConfigList { + repeated OpticalConfig opticalconfigs = 1; +} + +// ---- Optical Link ---- + +message OpticalLinkId { + Uuid optical_link_uuid = 1; +} + +message FiberId { + Uuid fiber_uuid = 1; +} + +message Fiber { + string ID = 10; + string src_port = 1; + string dst_port = 2; + string local_peer_port = 3; + string remote_peer_port = 4; + repeated int32 c_slots = 5; + repeated int32 l_slots = 6; + repeated int32 s_slots = 7; + float length = 8; + bool used = 9; + FiberId fiber_uuid = 11; + +} +message OpticalLinkDetails { + float length = 1; + string source = 2; + string target = 3; + repeated Fiber fibers = 4; +} + +message OpticalLink { + string name = 1; + OpticalLinkDetails details = 2; + OpticalLinkId optical_link_uuid = 3; +} diff --git a/proto/openconfig_device.proto b/proto/openconfig_device.proto new file mode 100644 index 0000000000000000000000000000000000000000..913ac247eaab89efb7d6de9f64b2730378937738 --- /dev/null +++ b/proto/openconfig_device.proto @@ -0,0 +1,23 @@ +// Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; +package openconfig_device; + +import "context.proto"; + +service OpenConfigService { + rpc AddOpenConfigDevice (context.OpticalConfig) returns (context.OpticalConfigId) {} + rpc ConfigureOpticalDevice(context.OpticalConfig) returns (context.Empty ) {} +} diff --git a/scripts/show_logs_opticalcontroller.sh b/scripts/show_logs_opticalcontroller.sh new file mode 100755 index 0000000000000000000000000000000000000000..17af5483c59e4d97a5534b388d006f5be00030e5 --- /dev/null +++ b/scripts/show_logs_opticalcontroller.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +######################################################################################################################## +# Define your deployment settings here +######################################################################################################################## + +# If not already set, set the name of the Kubernetes namespace to deploy to. +export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} + +######################################################################################################################## +# Automated steps start here +######################################################################################################################## + +kubectl --namespace $TFS_K8S_NAMESPACE logs deployment/opticalcontrollerservice -c server diff --git a/src/common/Constants.py b/src/common/Constants.py index c7ba01f69978fd3c601dcfe30180015d524b1100..a1913a951aeaa968a594fc3bafcb49581459a0e0 100644 --- a/src/common/Constants.py +++ b/src/common/Constants.py @@ -59,6 +59,7 @@ class ServiceNameEnum(Enum): TE = 'te' FORECASTER = 'forecaster' E2EORCHESTRATOR = 'e2eorchestrator' + OPTICALCONTROLLER = 'opticalcontroller' BGPLS = 'bgpls-speaker' # Used for test and debugging only @@ -87,6 +88,7 @@ DEFAULT_SERVICE_GRPC_PORTS = { ServiceNameEnum.TE .value : 10030, ServiceNameEnum.FORECASTER .value : 10040, ServiceNameEnum.E2EORCHESTRATOR .value : 10050, + ServiceNameEnum.OPTICALCONTROLLER .value : 10060, ServiceNameEnum.BGPLS .value : 20030, # Used for test and debugging only diff --git a/src/common/tools/descriptor/Tools.py b/src/common/tools/descriptor/Tools.py index b4a76ff4f00d0f6886895cca0ab6f27f7aa8aa43..e95fc756052bf792f0009518b19991b8e87bac68 100644 --- a/src/common/tools/descriptor/Tools.py +++ b/src/common/tools/descriptor/Tools.py @@ -15,6 +15,7 @@ import copy, json from typing import Dict, List, Optional, Tuple, Union from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import DeviceDriverEnum def get_descriptors_add_contexts(contexts : List[Dict]) -> List[Dict]: contexts_add = copy.deepcopy(contexts) @@ -95,7 +96,8 @@ def split_devices_by_rules(devices : List[Dict]) -> Tuple[List[Dict], List[Dict] if len(connect_rules) > 0: device_add = copy.deepcopy(device) - device_add['device_endpoints'] = [] + if (device['device_drivers'][0] != DeviceDriverEnum.DEVICEDRIVER_OC): + device_add['device_endpoints'] = [] device_add['device_config'] = {'config_rules': connect_rules} devices_add.append(device_add) diff --git a/src/context/client/ContextClient.py b/src/context/client/ContextClient.py index 13d9dc0035b45845bf11367e02c8830b5151c1d6..48a635cccd19f5985721a6a737fb8a2e5bd260b2 100644 --- a/src/context/client/ContextClient.py +++ b/src/context/client/ContextClient.py @@ -26,7 +26,9 @@ from common.proto.context_pb2 import ( Link, LinkEvent, LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, SliceFilter, SliceId, SliceIdList, SliceList, - Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList) + Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList, + OpticalConfig, OpticalConfigId, OpticalConfigList +) from common.proto.context_pb2_grpc import ContextServiceStub from common.proto.context_policy_pb2_grpc import ContextPolicyServiceStub from common.proto.policy_pb2 import PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule @@ -436,3 +438,26 @@ class ContextClient: response = self.policy_stub.RemovePolicyRule(request) LOGGER.debug('RemovePolicyRule result: {:s}'.format(grpc_message_to_json_string(response))) return response + + #//////////////// Experimental ////////////////// + + @RETRY_DECORATOR + def SetOpticalConfig(self, request : OpticalConfig) -> OpticalConfigId: + LOGGER.debug('SetOpticalConfig request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.SetOpticalConfig(request) + LOGGER.debug('SetOpticalConfig result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def GetOpticalConfig(self, request : Empty) -> OpticalConfigList: + LOGGER.debug('GetOpticalConfig request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.GetOpticalConfig(request) + LOGGER.debug('GetOpticalConfig result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def SelectOpticalConfig(self,request : OpticalConfigId) -> OpticalConfigList: + LOGGER.debug('SelectOpticalConfig request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.SelectOpticalConfig(request) + LOGGER.debug('SelectOpticalConfig result: {:s}'.format(grpc_message_to_json_string(response))) + return response diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index 5aad7f9c9ff3a6fd063b1f364256e760f47e1c33..a102fa17629bd866d96883230a542a6e7a4d92ff 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -23,7 +23,9 @@ from common.proto.context_pb2 import ( Link, LinkEvent, LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, SliceFilter, SliceId, SliceIdList, SliceList, - Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList) + Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList, + OpticalConfigList, OpticalConfigId, OpticalConfig +) from common.proto.policy_pb2 import PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule from common.proto.context_pb2_grpc import ContextServiceServicer from common.proto.context_policy_pb2_grpc import ContextPolicyServiceServicer @@ -43,6 +45,7 @@ from .database.Slice import ( slice_delete, slice_get, slice_list_ids, slice_list_objs, slice_select, slice_set, slice_unset) from .database.Topology import ( topology_delete, topology_get, topology_get_details, topology_list_ids, topology_list_objs, topology_set) +from .database.OpticalConfig import set_opticalconfig, select_opticalconfig, get_opticalconfig LOGGER = logging.getLogger(__name__) @@ -296,3 +299,22 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemovePolicyRule(self, request : PolicyRuleId, context: grpc.ServicerContext) -> Empty: return policyrule_delete(self.db_engine, self.messagebroker, request) + + # ---------------------------- Experimental ------------------- + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def GetOpticalConfig(self, request : Empty, context : grpc.ServicerContext) -> OpticalConfigList: + result = get_opticalconfig(self.db_engine) + return OpticalConfigList(OpticalConfigs=result) + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def SetOpticalConfig(self, request : OpticalConfig, context : grpc.ServicerContext) -> OpticalConfigId: + result = set_opticalconfig(self.db_engine, request) + return OpticalConfigId(**result) + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def SelectOpticalConfig(self, request : OpticalConfigId, context : grpc.ServicerContext) -> OpticalConfig: + result = select_opticalconfig(self.db_engine, request) + optical_config_id = OpticalConfigId() + optical_config_id.CopyFrom(result.OpticalConfig_id) + return OpticalConfig(config=result.config, OpticalConfig_id=optical_config_id) diff --git a/src/context/service/database/Device.py b/src/context/service/database/Device.py index 3aff20ade14532dcb7fbf8ec1033c084aaeead3c..737ad7add7a27580503e7f812e3064b879c191b5 100644 --- a/src/context/service/database/Device.py +++ b/src/context/service/database/Device.py @@ -21,7 +21,9 @@ from typing import Dict, List, Optional, Set, Tuple from common.method_wrappers.ServiceExceptions import InvalidArgumentException, NotFoundException from common.message_broker.MessageBroker import MessageBroker from common.proto.context_pb2 import ( - Device, DeviceFilter, DeviceId, DeviceIdList, DeviceList, Empty, EventTypeEnum, TopologyId) + Device, DeviceDriverEnum, DeviceFilter, DeviceId, DeviceIdList, DeviceList, + Empty, EventTypeEnum, TopologyId +) from common.tools.grpc.Tools import grpc_message_to_json_string from common.tools.object_factory.Device import json_device_id from context.service.database.uuids.Topology import topology_get_uuid @@ -103,10 +105,12 @@ def device_set(db_engine : Engine, messagebroker : MessageBroker, request : Devi }) topology_uuids.add(topology_uuid) + is_oc_driver = DeviceDriverEnum.DEVICEDRIVER_OC in set(request.device_drivers) + endpoints_data : List[Dict] = list() for i, endpoint in enumerate(request.device_endpoints): endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid - if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid + if len(endpoint_device_uuid) == 0 or is_oc_driver : endpoint_device_uuid = device_uuid if endpoint_device_uuid not in {raw_device_uuid, device_uuid}: raise InvalidArgumentException( 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, diff --git a/src/context/service/database/OpticalConfig.py b/src/context/service/database/OpticalConfig.py new file mode 100644 index 0000000000000000000000000000000000000000..9e7552bc111e40245bb649d2eb1ffa910c6f8588 --- /dev/null +++ b/src/context/service/database/OpticalConfig.py @@ -0,0 +1,88 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json, logging +from sqlalchemy.dialects.postgresql import insert +from sqlalchemy.engine import Engine +from sqlalchemy.orm import Session, sessionmaker +from sqlalchemy_cockroachdb import run_transaction +from common.proto.context_pb2 import OpticalConfig, OpticalConfigId +from .models.OpticalConfigModel import OpticalConfigModel + +LOGGER = logging.getLogger(__name__) + +def get_opticalconfig(db_engine : Engine): + def callback(session:Session): + optical_configs = list() + results = session.query(OpticalConfigModel).all() + for obj in results: + optical_config = OpticalConfig() + optical_config.config = json.dump(obj.config) + optical_config.opticalconfig_id.opticalconfig_uuid = obj.opticalconfig_uuid + optical_configs.append(optical_config) + return optical_configs + obj = run_transaction(sessionmaker(bind=db_engine), callback) + return obj + +def set_opticalconfig(db_engine : Engine, request : OpticalConfig): + opticalconfig_id = OpticalConfigId() + opticalconfig_id.opticalconfig_uuid = request.opticalconfig_id.opticalconfig_uuid + my_config_data = [] + if request.config: + channels = [] + transceivers = [] + config = json.loads(request.config) + if 'channels' in config and len(config['channels']) > 0: + channels = [channel['name']['index'] for channel in config['channels']] + if 'transceivers' in config and len(config['transceivers']['transceiver']) > 0: + transceivers = [transceiver for transceiver in config['transceivers']['transceiver']] + + my_config_data = [ + { + "opticalconfig_uuid": request.opticalconfig_id.opticalconfig_uuid, + "channels" : channels, + "transcievers" : transceivers, + "interfaces" : json.dumps(config["interfaces"]["interface"]), + "channel_namespace" : config["channel_namespace"], + "endpoints" : [json.dumps(endpoint) for endpoint in config["endpoints"]], + "frequency" : config["frequency"] if "frequency" in config else 0, + "operational_mode" : config["operational_mode"] if "operational_mode" in config else 0, + "output_power" : config["output_power"] if "output_power" in config else '', + } + ] + + def callback(session:Session)->bool: + stmt = insert(OpticalConfigModel).values(my_config_data) + stmt = stmt.on_conflict_do_update( + index_elements=[OpticalConfigModel.opticalconfig_uuid], + set_=dict( + channel_namespace=stmt.excluded.channel_namespace + ) + ) + stmt = stmt.returning(OpticalConfigModel.opticalconfig_uuid) + id = session.execute(stmt).fetchone() + opticalconfig_id = run_transaction(sessionmaker(bind=db_engine), callback) + return {'opticalconfig_uuid': opticalconfig_id} + +def select_opticalconfig(db_engine:Engine,request:OpticalConfigId): + def callback(session : Session) -> OpticalConfig: + result = OpticalConfig() + stmt = session.query(OpticalConfigModel) + stmt = stmt.filter_by(opticalconfig_uuid=request.opticalconfig_uuid) + obj = stmt.first() + if obj is not None: + result.config = json.dumps(obj.dump()) + result.opticalconfig_id.opticalconfig_uuid = obj.opticalconfig_uuid + return result + return run_transaction(sessionmaker(bind=db_engine, expire_on_commit=False), callback) diff --git a/src/context/service/database/Service.py b/src/context/service/database/Service.py index fc196ddded291aa82c8f9df932c15611d13121e4..337bf592f785db97d5924aa7da17772c35eac1b2 100644 --- a/src/context/service/database/Service.py +++ b/src/context/service/database/Service.py @@ -20,7 +20,9 @@ from sqlalchemy.orm import Session, selectinload, sessionmaker from sqlalchemy_cockroachdb import run_transaction from typing import Dict, List, Optional, Set from common.proto.context_pb2 import ( - ContextId, Empty, EventTypeEnum, Service, ServiceFilter, ServiceId, ServiceIdList, ServiceList) + ContextId, Empty, EventTypeEnum, Service, ServiceFilter, ServiceId, ServiceIdList, + ServiceList, ServiceTypeEnum +) from common.message_broker.MessageBroker import MessageBroker from common.method_wrappers.ServiceExceptions import InvalidArgumentException, NotFoundException from common.tools.object_factory.Context import json_context_id @@ -84,6 +86,9 @@ def service_set(db_engine : Engine, messagebroker : MessageBroker, request : Ser context_uuid,service_uuid = service_get_uuid(request.service_id, service_name=service_name, allow_random=True) service_type = grpc_to_enum__service_type(request.service_type) + if service_type is None and request.service_type == ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY: + service_type = "OPTICAL_CONNECTIVITY" + service_status = grpc_to_enum__service_status(request.service_status.service_status) now = datetime.datetime.utcnow() diff --git a/src/context/service/database/models/OpticalConfigModel.py b/src/context/service/database/models/OpticalConfigModel.py new file mode 100644 index 0000000000000000000000000000000000000000..10cf197f9a8a728b8fd02bdcdaf255677551bf17 --- /dev/null +++ b/src/context/service/database/models/OpticalConfigModel.py @@ -0,0 +1,42 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from sqlalchemy import Column, String, Integer +from sqlalchemy.dialects.postgresql import ARRAY +from ._Base import _Base + +class OpticalConfigModel(_Base): + __tablename__ = 'optical_config' + opticalconfig_uuid = Column(String, primary_key=True) + channels = Column(ARRAY(String), nullable=True) + transcievers = Column(ARRAY(String), nullable=True) + interfaces = Column(String, nullable=True) + channel_namespace = Column(String, nullable=True) + endpoints = Column(ARRAY(String), nullable=True) + frequency = Column(Integer, nullable=True) + operational_mode = Column(Integer, nullable=True) + output_power = Column(String, nullable=True) + + def dump(self): + return { + "channels" : [{'name': {'index': channel}} for channel in self.channels], + "transceivers" : {"transceiver": [transciever for transciever in self.transcievers]}, + "interfaces" : {"interface": json.loads(self.interfaces)}, + "channel_namespace" : self.channel_namespace, + "endpoints" : [json.loads(endpoint) for endpoint in self.endpoints], + "frequency" : self.frequency, + "output_power" : self.output_power, + "operational_mode" : self.operational_mode, + } diff --git a/src/context/service/database/models/enums/DeviceDriver.py b/src/context/service/database/models/enums/DeviceDriver.py index 8e15bf058599eeed0629fc1249af0d052183db28..2ccdda2725a7f7bb13ba296d4eca25f88b1e73d1 100644 --- a/src/context/service/database/models/enums/DeviceDriver.py +++ b/src/context/service/database/models/enums/DeviceDriver.py @@ -33,6 +33,7 @@ class ORM_DeviceDriverEnum(enum.Enum): GNMI_OPENCONFIG = DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG FLEXSCALE = DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE IETF_ACTN = DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN + OC = DeviceDriverEnum.DEVICEDRIVER_OC grpc_to_enum__device_driver = functools.partial( grpc_to_enum, DeviceDriverEnum, ORM_DeviceDriverEnum) diff --git a/src/context/service/database/models/enums/ServiceType.py b/src/context/service/database/models/enums/ServiceType.py index ce198f8c1f795f25da547e2d5974059062489709..01ed68c1708dc617a46edc31e94fc07d79e278e1 100644 --- a/src/context/service/database/models/enums/ServiceType.py +++ b/src/context/service/database/models/enums/ServiceType.py @@ -28,6 +28,7 @@ class ORM_ServiceTypeEnum(enum.Enum): TAPI_CONNECTIVITY_SERVICE = ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE TE = ServiceTypeEnum.SERVICETYPE_TE E2E = ServiceTypeEnum.SERVICETYPE_E2E + OPTICAL_CONNECTIVITY = ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY grpc_to_enum__service_type = functools.partial( grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum) diff --git a/src/device/client/DeviceClient.py b/src/device/client/DeviceClient.py index b88727983474f1f815caa959b8ee8ccfb5b3623b..3fde3df778e32df20c863ff110a1eb572f38d177 100644 --- a/src/device/client/DeviceClient.py +++ b/src/device/client/DeviceClient.py @@ -15,12 +15,12 @@ import grpc, logging from common.Constants import ServiceNameEnum from common.Settings import get_service_host, get_service_port_grpc -from common.proto.context_pb2 import Device, DeviceConfig, DeviceId, Empty +from common.proto.context_pb2 import Device, DeviceConfig, DeviceId, Empty,OpticalConfig,OpticalConfigId from common.proto.device_pb2 import MonitoringSettings from common.proto.device_pb2_grpc import DeviceServiceStub from common.tools.client.RetryDecorator import retry, delay_exponential from common.tools.grpc.Tools import grpc_message_to_json_string - +from common.proto.openconfig_device_pb2_grpc import OpenConfigServiceStub LOGGER = logging.getLogger(__name__) MAX_RETRIES = 15 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0) @@ -34,12 +34,14 @@ class DeviceClient: LOGGER.debug('Creating channel to {:s}...'.format(str(self.endpoint))) self.channel = None self.stub = None + self.openconfig_stub=None self.connect() LOGGER.debug('Channel created') def connect(self): self.channel = grpc.insecure_channel(self.endpoint) self.stub = DeviceServiceStub(self.channel) + self.openconfig_stub=OpenConfigServiceStub(self.channel) def close(self): if self.channel is not None: self.channel.close() @@ -80,3 +82,8 @@ class DeviceClient: response = self.stub.MonitorDeviceKpi(request) LOGGER.debug('MonitorDeviceKpi result: {:s}'.format(grpc_message_to_json_string(response))) return response + def ConfigureOpticalDevice(self, request : OpticalConfig) -> OpticalConfigId: + LOGGER.debug('ConfigureOpticalDevice request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.openconfig_stub.ConfigureOpticalDevice(request) + LOGGER.debug('ConfigureOpticalDevice result: {:s}'.format(grpc_message_to_json_string(response))) + return response diff --git a/src/device/service/DeviceService.py b/src/device/service/DeviceService.py index 6d27ef96eef4b93fa7d6ca294d1fd645e815af03..c7868e44579fb27b6eecc6806f64e3bb531d7cd4 100644 --- a/src/device/service/DeviceService.py +++ b/src/device/service/DeviceService.py @@ -19,6 +19,8 @@ from common.tools.service.GenericGrpcService import GenericGrpcService from .driver_api.DriverInstanceCache import DriverInstanceCache from .DeviceServiceServicerImpl import DeviceServiceServicerImpl from .monitoring.MonitoringLoops import MonitoringLoops +from .OpenConfigServicer import OpenConfigServicer +from common.proto.openconfig_device_pb2_grpc import add_OpenConfigServiceServicer_to_server # Custom gRPC settings # Multiple clients might keep connections alive waiting for RPC methods to be executed. @@ -31,10 +33,12 @@ class DeviceService(GenericGrpcService): super().__init__(port, max_workers=GRPC_MAX_WORKERS, cls_name=cls_name) self.monitoring_loops = MonitoringLoops() self.device_servicer = DeviceServiceServicerImpl(driver_instance_cache, self.monitoring_loops) + self.openconfig_device_servicer=OpenConfigServicer(driver_instance_cache,self.monitoring_loops) def install_servicers(self): self.monitoring_loops.start() add_DeviceServiceServicer_to_server(self.device_servicer, self.server) + add_OpenConfigServiceServicer_to_server(self.openconfig_device_servicer,self.server) def stop(self): super().stop() diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py index 3df7c482272804eb2589ed1e7569f0a2e822ad21..76fb6545430f4802d7d37c0c35a35a0539aa200e 100644 --- a/src/device/service/DeviceServiceServicerImpl.py +++ b/src/device/service/DeviceServiceServicerImpl.py @@ -20,7 +20,9 @@ from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, get_env_var_name from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, safe_and_metered_rpc_method from common.method_wrappers.ServiceExceptions import NotFoundException, OperationFailedException from common.proto.context_pb2 import ( - Device, DeviceConfig, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Link) + Device, DeviceConfig, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Link, + OpticalConfig, OpticalConfigId +) from common.proto.device_pb2 import MonitoringSettings from common.proto.device_pb2_grpc import DeviceServiceServicer from common.tools.context_queries.Device import get_device @@ -29,6 +31,7 @@ from context.client.ContextClient import ContextClient from .driver_api._Driver import _Driver from .driver_api.DriverInstanceCache import DriverInstanceCache, get_driver from .monitoring.MonitoringLoops import MonitoringLoops +from .drivers.oc_driver.OCDriver import OCDriver from .ErrorMessages import ERROR_MISSING_DRIVER, ERROR_MISSING_KPI from .Tools import ( check_connect_rules, check_no_endpoints, compute_rules_to_add_delete, configure_rules, deconfigure_rules, @@ -58,7 +61,8 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): device_uuid = request.device_id.device_uuid.uuid connection_config_rules = check_connect_rules(request.device_config) - check_no_endpoints(request.device_endpoints) + if request.device_drivers[0] != DeviceDriverEnum.DEVICEDRIVER_OC: + check_no_endpoints(request.device_endpoints) t1 = time.time() @@ -138,6 +142,13 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): else: # ZTP is not deployed; assume the device is ready while onboarding and set them as enabled. device.device_operational_status = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED + + # temporary line + if request.device_drivers[0] == DeviceDriverEnum.DEVICEDRIVER_OC and len(request.device_endpoints) > 0: + #for endpoint in request.device_endpoints: + # #endpoint.endpoint_id.device_id.CopyFrom(device.device_id) + # pass + device.device_endpoints.extend(request.device_endpoints) device_id = context_client.SetDevice(device) diff --git a/src/device/service/OpenConfigServicer.py b/src/device/service/OpenConfigServicer.py new file mode 100644 index 0000000000000000000000000000000000000000..1060449f185330faa0a71ff062ad2bbd0086b2e4 --- /dev/null +++ b/src/device/service/OpenConfigServicer.py @@ -0,0 +1,95 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import grpc, logging, json +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method +from common.method_wrappers.ServiceExceptions import NotFoundException +from common.proto.context_pb2 import ( + Device, DeviceId, DeviceOperationalStatusEnum, Empty, OpticalConfig, OpticalConfig +) +from common.proto.device_pb2_grpc import DeviceServiceServicer +from common.tools.context_queries.Device import get_device +from common.tools.mutex_queues.MutexQueues import MutexQueues +from context.client.ContextClient import ContextClient +from .driver_api._Driver import _Driver +from .driver_api.DriverInstanceCache import DriverInstanceCache, get_driver +from .monitoring.MonitoringLoops import MonitoringLoops +from .Tools import extract_resources +from .Tools import check_no_endpoints + +LOGGER = logging.getLogger(__name__) + +METRICS_POOL = MetricsPool('Device', 'RPC') + +METRICS_POOL_DETAILS = MetricsPool('Device', 'execution', labels={ + 'driver': '', 'operation': '', 'step': '', +}) + +class OpenConfigServicer(DeviceServiceServicer): + def __init__(self, driver_instance_cache : DriverInstanceCache, monitoring_loops : MonitoringLoops) -> None: + LOGGER.debug('Creating Servicer...') + self.driver_instance_cache = driver_instance_cache + self.monitoring_loops = monitoring_loops + self.mutex_queues = MutexQueues() + LOGGER.debug('Servicer Created') + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def AddOpenConfigDevice(self, request : OpticalConfig, context : grpc.ServicerContext) -> DeviceId: + device_uuid = request.device_id.device_uuid.uuid + check_no_endpoints(request.device_endpoints) + + context_client = ContextClient() + device = get_device(context_client, device_uuid, rw_copy=True) + if device is None: + # not in context, create blank one to get UUID, and populate it below + device = Device() + device.device_id.CopyFrom(request.device_id) # pylint: disable=no-member + device.name = request.name + device.device_type = request.device_type + device.device_operational_status = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED + device.device_drivers.extend(request.device_drivers) # pylint: disable=no-member + device.device_config.CopyFrom(request.device_config) + device.device_endpoints.extend(request.device_endpoints) + # pylint: disable=no-member + device_id = context_client.SetDevice(device) + device = get_device(context_client, device_id.device_uuid.uuid, rw_copy=True) + + # update device_uuid to honor UUID provided by Context + device_uuid = device.device_id.device_uuid.uuid + self.mutex_queues.wait_my_turn(device_uuid) + try: + device_id = context_client.SetDevice(device) + except Exception as error : + LOGGER.debug("error %s",error) + + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def ConfigureOpticalDevice (self, request : OpticalConfig, context : grpc.ServicerContext) -> Empty: + device_uuid = request.opticalconfig_id.opticalconfig_uuid + resources=[] + config =json.loads(request.config) + try: + context_client = ContextClient() + device = get_device( + context_client, device_uuid, rw_copy=True, include_endpoints=True, include_components=False, + include_config_rules=False) + if device is None: + raise NotFoundException('Device', device_uuid, extra_details='loading in ConfigureDevice') + resources,conditions=extract_resources(config=config,device=device) + driver : _Driver = get_driver(self.driver_instance_cache, device) + result = driver.SetConfig(resources=resources,conditions=conditions) + #TODO: add a control with the NETCONF get + #driver.GetConfig(resource_keys=filter_fields) + except Exception as e: + LOGGER.info("error in configuring %s",e) + return Empty() diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index b2b206471e07b654e5339f81db632699ae8b95df..f3700e10488228387145a2c4c8574a899675aade 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -15,12 +15,14 @@ import json, logging from typing import Any, Dict, List, Optional, Tuple, Union from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME -from common.method_wrappers.ServiceExceptions import InvalidArgumentException -from common.proto.context_pb2 import ConfigActionEnum, ConfigRule_ACL, Device, DeviceConfig, Link, Location +from common.DeviceTypes import DeviceTypeEnum +from common.method_wrappers.ServiceExceptions import InvalidArgumentException, NotFoundException +from common.proto.context_pb2 import ConfigActionEnum, ConfigRule_ACL, Device, DeviceConfig, EndPoint, Link, Location from common.proto.device_pb2 import MonitoringSettings from common.proto.kpi_sample_types_pb2 import KpiSampleType from common.tools.grpc.ConfigRules import update_config_rule_custom from common.tools.grpc.Tools import grpc_message_to_json +from common.type_checkers.Checkers import chk_length, chk_type from .driver_api._Driver import _Driver, RESOURCE_ENDPOINTS from .monitoring.MonitoringLoops import MonitoringLoops from .ErrorMessages import ( @@ -30,6 +32,21 @@ from .ErrorMessages import ( LOGGER = logging.getLogger(__name__) +def get_endpoint_matching(device : Device, endpoint_uuid_or_name : str) -> EndPoint: + for endpoint in device.device_endpoints: + choices = {endpoint.endpoint_id.endpoint_uuid.uuid, endpoint.name} + if endpoint_uuid_or_name in choices: return endpoint + + device_uuid = device.device_id.device_uuid.uuid + extra_details = 'Device({:s})'.format(str(device_uuid)) + raise NotFoundException('Endpoint', endpoint_uuid_or_name, extra_details=extra_details) + +def get_device_endpoint_uuids(endpoint : Tuple[str, str, Optional[str]]) -> Tuple[str, str]: + chk_type('endpoint', endpoint, (tuple, list)) + chk_length('endpoint', endpoint, min_length=2, max_length=3) + device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now + return device_uuid, endpoint_uuid + def check_connect_rules(device_config : DeviceConfig) -> Dict[str, Any]: connection_config_rules = dict() unexpected_config_rules = list() @@ -434,3 +451,74 @@ def update_endpoints(src_device : Device, dst_device : Device) -> None: dst_topology_id = dst_endpoint_id.topology_id if len(src_topology_uuid) > 0: dst_topology_id.topology_uuid.uuid = src_topology_uuid if len(src_context_uuid) > 0: dst_topology_id.context_id.context_uuid.uuid = src_context_uuid + +def get_edit_target(device : Device, is_opticalband : bool) -> str: + if is_opticalband: return 'optical-band' + if device.device_type == DeviceTypeEnum.OPTICAL_ROADM: return 'media-channel' + return 'optical-channel' + +def is_key_existed(key : str, keys_dic = dict, key_name_to_use = None) -> dict: + dic = {} + dic['resource_key'] = key + if key_name_to_use is not None: + dic['resource_key'] = key_name_to_use + if key in keys_dic: + dic['value'] = keys_dic[key] + else: + dic['value'] = None + return dic + +def extract_resources(config : dict, device : Device) -> list: + conditions = {} + resources = [] + resources.append(is_key_existed('channel_namespace', config)) + resources.append(is_key_existed('add_transceiver', config)) + is_opticalband = config.get('is_opticalband', False) + conditions['is_opticalband'] = is_opticalband + conditions['edit_type'] = get_edit_target(device, is_opticalband) + if 'flow' in config: + #for tuple_value in config['flow'][device.name]: + source_vals = [] + dest_vals = [] + for tuple_value in config['flow']: + source_port = None + destination_port = None + source_port_uuid, destination_port_uuid = tuple_value + if source_port_uuid != '0': + src_endpoint_obj = get_endpoint_matching(device, source_port_uuid) + source_port = src_endpoint_obj.name + source_vals.append(source_port) + if destination_port_uuid != '0': + dst_endpoint_obj = get_endpoint_matching(device, destination_port_uuid) + destination_port = dst_endpoint_obj.name + dest_vals.append(destination_port) + resources.append({'resource_key': 'source_port', 'value': source_vals}) + resources.append({'resource_key': 'destination_port', 'value': dest_vals }) + + if 'new_config' in config: + lower_frequency = None + upper_frequency = None + resources.append(is_key_existed('target-output-power', keys_dic=config['new_config'])) + resources.append(is_key_existed('frequency', keys_dic=config['new_config'])) + resources.append(is_key_existed('operational-mode', keys_dic=config['new_config'])) + resources.append(is_key_existed('line-port', keys_dic=config['new_config'])) + resources.append(is_key_existed('band_type', keys_dic=config['new_config'], key_name_to_use='name')) + resources.append(is_key_existed('ob_id', keys_dic=config['new_config'], key_name_to_use='optical-band-parent')) + resources.append(is_key_existed('name', keys_dic=config['new_config'], key_name_to_use='channel_name')) + if not is_opticalband: + if 'frequency' in config['new_config'] and 'band' in config['new_config'] and conditions['edit_type'] == 'media-channel': + lower_frequency = int(int(config['new_config']['frequency']) - (int(config['new_config']['band'])/2)) + upper_frequency = int(int(config['new_config']['frequency']) + (int(config['new_config']['band'])/2)) + #lower_frequency = (config['new_config']['frequency'] - config['new_config']['band'])/2 + #upper_frequency = (config['new_config']['frequency'] + config['new_config']['band'])/2 + resources.append(is_key_existed('flow_id', keys_dic=config['new_config'], key_name_to_use='index')) + #resources.append({'resource_key':'index','value':config['new_config']['flow_id'] if 'flow_id' in config['new_config'] else None}) + else: + lower_frequency = config['new_config']['low-freq'] if 'low-freq' in config['new_config'] else None + upper_frequency = config['new_config']['up-freq' ] if 'up-freq' in config['new_config'] else None + resources.append(is_key_existed('ob_id', keys_dic=config['new_config'], key_name_to_use='index')) + #resources.append({'resource_key':'index','value':config['new_config']['ob_id'] if 'ob_id' in config['new_config'] else None}) + resources.append({'resource_key': 'lower-frequency', 'value': lower_frequency}) + resources.append({'resource_key': 'upper-frequency', 'value': upper_frequency}) + + return [resources, conditions] diff --git a/src/device/service/driver_api/DriverInstanceCache.py b/src/device/service/driver_api/DriverInstanceCache.py index 1f92059a63889c002eb28ca7eaecc43199f66794..26735bc16a4f0d70c9299e206bda07c2b10e8c49 100644 --- a/src/device/service/driver_api/DriverInstanceCache.py +++ b/src/device/service/driver_api/DriverInstanceCache.py @@ -52,7 +52,12 @@ class DriverInstanceCache: driver_class = self._driver_factory.get_driver_class(**filter_fields) MSG = 'Driver({:s}) selected for device({:s}) with filter_fields({:s})...' LOGGER.info(MSG.format(str(driver_class.__name__), str(device_uuid), str(filter_fields))) - driver_instance : _Driver = driver_class(address, port, **settings) + + if driver_class.__name__ == "OCDriver": + driver_instance : _Driver = driver_class(address, port, device_uuid=device_uuid, **settings) + else: + driver_instance : _Driver = driver_class(address, port, **settings) + self._device_uuid__to__driver_instance[device_uuid] = driver_instance return driver_instance diff --git a/src/device/service/drivers/__init__.py b/src/device/service/drivers/__init__.py index 27c61f89f15c735b44ad2724df01e08a51dda6ba..1e8cff6052d840af2aa57ff22793380f06f16356 100644 --- a/src/device/service/drivers/__init__.py +++ b/src/device/service/drivers/__init__.py @@ -167,3 +167,17 @@ if LOAD_ALL_DEVICE_DRIVERS: FilterFieldEnum.DRIVER: DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE, } ])) + +if LOAD_ALL_DEVICE_DRIVERS: + from .oc_driver.OCDriver import OCDriver # pylint: disable=wrong-import-position + DRIVERS.append( + (OCDriver, [ + { + # Real Packet Router, specifying OpenConfig Driver => use OpenConfigDriver + FilterFieldEnum.DEVICE_TYPE: [ + DeviceTypeEnum.OPTICAL_ROADM, + DeviceTypeEnum.OPTICAL_TRANSPONDER + ], + FilterFieldEnum.DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC, + } + ])) diff --git a/src/device/service/drivers/oc_driver/OCDriver.py b/src/device/service/drivers/oc_driver/OCDriver.py new file mode 100644 index 0000000000000000000000000000000000000000..16f00cfb4c7b80d807882ab5b4c95b36b28db8f6 --- /dev/null +++ b/src/device/service/drivers/oc_driver/OCDriver.py @@ -0,0 +1,315 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import anytree, copy, logging, pytz, queue, re, threading +#import lxml.etree as ET +from datetime import datetime, timedelta +from typing import Any, Dict, Iterator, List, Optional, Tuple, Union +from apscheduler.executors.pool import ThreadPoolExecutor +import xml.etree.ElementTree as ET +from apscheduler.job import Job +from apscheduler.jobstores.memory import MemoryJobStore +from apscheduler.schedulers.background import BackgroundScheduler +from ncclient.manager import Manager, connect_ssh +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.tools.client.RetryDecorator import delay_exponential +from common.type_checkers.Checkers import chk_length, chk_string, chk_type, chk_float +from device.service.driver_api.Exceptions import UnsupportedResourceKeyException +from device.service.driver_api._Driver import _Driver +from device.service.driver_api.AnyTreeTools import TreeNode, get_subnode, set_subnode_value #dump_subtree +#from .Tools import xml_pretty_print, xml_to_dict, xml_to_file +from .templates.Interfaces.interfaces import interface_template +from .templates.VPN.physical import create_optical_channel,add_transceiver,create_media_channel,create_optical_band +from .RetryDecorator import retry +from context.client.ContextClient import ContextClient +from common.proto.context_pb2 import ( + OpticalConfig, + ConfigActionEnum, Device, DeviceDriverEnum, DeviceId, DeviceList, DeviceOperationalStatusEnum, Empty + ,OpticalConfigId,Uuid) +from .templates.Tools import extractor +from .Tools import generate_uuid_from_numbers +DEBUG_MODE = False +logging.getLogger('ncclient.manager').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING) +logging.getLogger('ncclient.transport.ssh').setLevel(logging.DEBUG if DEBUG_MODE else logging.WARNING) +logging.getLogger('apscheduler.executors.default').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR) +logging.getLogger('apscheduler.scheduler').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR) +logging.getLogger('monitoring-client').setLevel(logging.INFO if DEBUG_MODE else logging.ERROR) + +RE_GET_ENDPOINT_FROM_INTERFACE_KEY = re.compile(r'.*interface\[([^\]]+)\].*') +RE_GET_ENDPOINT_FROM_INTERFACE_XPATH = re.compile(r".*interface\[oci\:name\='([^\]]+)'\].*") + +# Collection of samples through NetConf is very slow and each request collects all the data. +# Populate a cache periodically (when first interface is interrogated). +# Evict data after some seconds, when data is considered as outdated + +SAMPLE_EVICTION_SECONDS = 30.0 # seconds +SAMPLE_RESOURCE_KEY = 'interfaces/interface/state/counters' +filter_fields= ["frequency","target-output-power","interface","operational-mode","line-port"] +MAX_RETRIES = 15 +DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0) +RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') +context_client= ContextClient() +port_xml_filter=f"/components/component[state[type='oc-platform-types:PORT']]/*" +transceiver_xml_filter="/components/component[state[type='oc-platform-types:TRANSCEIVER']]/*" +class NetconfSessionHandler: + def __init__(self, address : str, port : int, **settings) -> None: + self.__lock = threading.RLock() + self.__connected = threading.Event() + self.__address = address + self.__port = int(port) + self.__username = settings.get('username') + self.__password = settings.get('password') + self.__vendor = settings.get('vendor') + self.__version = settings.get('version', "1") + self.__key_filename = settings.get('key_filename') + self.__hostkey_verify = settings.get('hostkey_verify', True) + self.__look_for_keys = settings.get('look_for_keys', True) + self.__allow_agent = settings.get('allow_agent', True) + self.__force_running = settings.get('force_running', False) + self.__commit_per_rule = settings.get('commit_per_rule', False) + self.__device_params = settings.get('device_params', {}) + self.__manager_params = settings.get('manager_params', {}) + self.__nc_params = settings.get('nc_params', {}) + self.__message_renderer = settings.get('message_renderer','jinja') + self.__manager : Manager = None + self.__candidate_supported = False + + def connect(self): + with self.__lock: + self.__manager = connect_ssh( + host=self.__address, port=self.__port, username=self.__username, password=self.__password, + device_params=self.__device_params, manager_params=self.__manager_params, nc_params=self.__nc_params, + key_filename=self.__key_filename, hostkey_verify=self.__hostkey_verify, allow_agent=self.__allow_agent, + look_for_keys=self.__look_for_keys) + self.__candidate_supported = ':candidate' in self.__manager.server_capabilities + self.__connected.set() + + def disconnect(self): + if not self.__connected.is_set(): return + with self.__lock: + self.__manager.close_session() + + @property + def use_candidate(self): return self.__candidate_supported and not self.__force_running + + @property + def commit_per_rule(self): return self.__commit_per_rule + + @property + def vendor(self): return self.__vendor + + @property + def version(self): return self.__version + + @property + def message_renderer(self): return self.__message_renderer + + @RETRY_DECORATOR + def get(self, filter=None, with_defaults=None): # pylint: disable=redefined-builtin + with self.__lock: + config=self.__manager.get(filter=filter, with_defaults=with_defaults) + + return config + + @RETRY_DECORATOR + def edit_config( + self, config, target='running', default_operation=None, test_option=None, + error_option=None, format='xml' # pylint: disable=redefined-builtin + ): + + + + with self.__lock: + response= self.__manager.edit_config( + config, target=target, default_operation=default_operation, test_option=test_option, + error_option=error_option, format=format) + + + @RETRY_DECORATOR + def locked(self, target): + return self.__manager.locked(target=target) + + @RETRY_DECORATOR + def commit(self, confirmed=False, timeout=None, persist=None, persist_id=None): + return self.__manager.commit(confirmed=confirmed, timeout=timeout, persist=persist, persist_id=persist_id) + +DRIVER_NAME = 'oc' +METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': DRIVER_NAME}) +def edit_config( # edit the configuration of openconfig devices + netconf_handler : NetconfSessionHandler, logger : logging.Logger, resources : List[Tuple[str, Any]] + ,conditions, delete=False, + commit_per_rule=False, target='running', default_operation='merge', test_option=None, error_option=None, + format='xml' +): + #str_method = 'DeleteConfig' if delete else 'SetConfig' + results = [] + + str_config_messages=[] + + + if (conditions['edit_type']=='optical-channel'): + #transponder + str_config_messages = create_optical_channel(resources) + elif (conditions['edit_type']=='optical-band'): + #roadm optical-band + str_config_messages = create_optical_band(resources) + else : + #roadm media-channel + str_config_messages=create_media_channel(resources) + + + + for str_config_message in str_config_messages: + # configuration of the received templates + if str_config_message is None: raise UnsupportedResourceKeyException("CONFIG") + + netconf_handler.edit_config( # configure the device + config=str_config_message, target=target, default_operation=default_operation, + test_option=test_option, error_option=error_option, format=format) + if commit_per_rule: + netconf_handler.commit() # configuration commit + + #results[i] = True + results.append(True) + + +class OCDriver(_Driver): + def __init__(self, address : str, port : int,device_uuid=None, **settings) -> None: + super().__init__(DRIVER_NAME, address, port, **settings) + self.__logger = logging.getLogger('{:s}:[{:s}:{:s}]'.format(str(__name__), str(self.address), str(self.port))) + self.__lock = threading.Lock() + #self.__initial = TreeNode('.') + #self.__running = TreeNode('.') + self.__subscriptions = TreeNode('.') + self.__started = threading.Event() + self.__terminate = threading.Event() + self.__scheduler = BackgroundScheduler(daemon=True) # scheduler used to emulate sampling events + self.__scheduler.configure( + jobstores = {'default': MemoryJobStore()}, + executors = {'default': ThreadPoolExecutor(max_workers=1)}, # important! 1 = avoid concurrent requests + job_defaults = {'coalesce': False, 'max_instances': 3}, + timezone=pytz.utc) + self._temp_address=f"{address}{port}" + self.__out_samples = queue.Queue() + self.__netconf_handler = NetconfSessionHandler(self.address, self.port, **(self.settings)) + + self.__device_uuid=device_uuid + + self.Connect() + #self.GetConfig() + #self.__samples_cache = SamplesCache(self.__netconf_handler, self.__logger) + + def Connect(self) -> bool: + with self.__lock: + if self.__started.is_set(): return True + self.__netconf_handler.connect() + # Connect triggers activation of sampling events that will be scheduled based on subscriptions + self.__scheduler.start() + self.__started.set() + return True + + def Disconnect(self) -> bool: + with self.__lock: + # Trigger termination of loops and processes + self.__terminate.set() + # If not started, assume it is already disconnected + if not self.__started.is_set(): return True + # Disconnect triggers deactivation of sampling events + self.__scheduler.shutdown() + self.__netconf_handler.disconnect() + return True + + @metered_subclass_method(METRICS_POOL) + def GetInitialConfig(self) -> List[Tuple[str, Any]]: + with self.__lock: + return [] + + @metered_subclass_method(METRICS_POOL) + def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: + + + chk_type('resources', resource_keys, list) + results = [] + opticalConfig= OpticalConfig() + j=0 + + with self.__lock: + + + context_client.connect() + config={} + channels_lst=[] + transceivers={} + + try: + xml_data = self.__netconf_handler.get().data_xml + transceivers,interfaces,channels_lst,channel_namespace,endpoints=extractor(data_xml=xml_data,resource_keys=filter_fields,dic=config) + + + except Exception as e: # pylint: disable=broad-except + MSG = 'Exception retrieving {:s}' + self.__logger.info("error from getConfig %s",e) + self.__logger.exception(MSG.format(e)) + #results.append((resource_key, e)) # if validation fails, store the exception + + #///////////////////////// divider //////////////////////////////////////////////////////// + + + value_dic={} + value_dic["channels"]=channels_lst + value_dic["transceivers"]=transceivers + value_dic["interfaces"]=interfaces + value_dic["channel_namespace"]=channel_namespace + value_dic["endpoints"]=endpoints + + opticalConfig.config=json.dumps(value_dic) + opticalConfig.opticalconfig_id.opticalconfig_uuid=self.__device_uuid if self.__device_uuid is not None else "" + config_id=context_client.SetOpticalConfig(opticalConfig) + + context_client.close() + + return results + + @metered_subclass_method(METRICS_POOL) + def SetConfig(self, resources : List[Tuple[str, Any]],conditions:dict) -> List[Union[bool, Exception]]: + if len(resources) == 0: return [] + results=[] + with self.__lock: + if self.__netconf_handler.use_candidate: + with self.__netconf_handler.locked(target='candidate'): + results = edit_config( + self.__netconf_handler, self.__logger, resources,conditions, target='candidate', + commit_per_rule=self.__netconf_handler.commit_per_rule + ,) + else: + results = edit_config(self.__netconf_handler, self.__logger, resources,conditions=conditions + ) + return results + + @metered_subclass_method(METRICS_POOL) + def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + with self.__lock: + if self.__netconf_handler.use_candidate: + with self.__netconf_handler.locked(target='candidate'): + results = edit_config( + self.__netconf_handler, self.__logger, resources, target='candidate', delete=True, + commit_per_rule=self.__netconf_handler.commit_per_rule) + else: + results = edit_config(self.__netconf_handler, self.__logger, resources, delete=True) + return results + + diff --git a/src/device/service/drivers/oc_driver/RetryDecorator.py b/src/device/service/drivers/oc_driver/RetryDecorator.py new file mode 100644 index 0000000000000000000000000000000000000000..deb1b4ed89346a99e9821f049375a1977914832b --- /dev/null +++ b/src/device/service/drivers/oc_driver/RetryDecorator.py @@ -0,0 +1,46 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging, time +from common.tools.client.RetryDecorator import delay_linear + +LOGGER = logging.getLogger(__name__) + +def retry(max_retries=0, delay_function=delay_linear(initial=0, increment=0), + prepare_method_name=None, prepare_method_args=[], prepare_method_kwargs={}): + def _reconnect(func): + def wrapper(self, *args, **kwargs): + if prepare_method_name is not None: + prepare_method = getattr(self, prepare_method_name, None) + if prepare_method is None: raise Exception('Prepare Method ({}) not found'.format(prepare_method_name)) + num_try, given_up = 0, False + while not given_up: + try: + return func(self, *args, **kwargs) + except OSError as e: + if str(e) != 'Socket is closed': raise + + num_try += 1 + given_up = num_try > max_retries + if given_up: raise Exception('Giving up... {:d} tries failed'.format(max_retries)) from e + if delay_function is not None: + delay = delay_function(num_try) + time.sleep(delay) + LOGGER.info('Retry {:d}/{:d} after {:f} seconds...'.format(num_try, max_retries, delay)) + else: + LOGGER.info('Retry {:d}/{:d} immediate...'.format(num_try, max_retries)) + + if prepare_method_name is not None: prepare_method(*prepare_method_args, **prepare_method_kwargs) + return wrapper + return _reconnect diff --git a/src/device/service/drivers/oc_driver/Tools.py b/src/device/service/drivers/oc_driver/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..d350fd877efcff04ec23f90341fca727b6177ba5 --- /dev/null +++ b/src/device/service/drivers/oc_driver/Tools.py @@ -0,0 +1,38 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import hashlib +import uuid +import xml.dom.minidom, xmltodict + +def xml_pretty_print(data : str): + return xml.dom.minidom.parseString(data).toprettyxml() + +def xml_to_file(data : str, file_path : str) -> None: + with open(file_path, mode='w', encoding='UTF-8') as f: + f.write(xml_pretty_print(data)) + +def xml_to_dict(data : str): + return xmltodict.parse(data) + +def generate_uuid_from_numbers(code:str) ->str: + # Concatenate the numbers into a single string + + + # Generate a hash value using MD5 algorithm + hash_value = hashlib.md5(code.encode()).hexdigest() + + # Convert the hash value into a UUID + generated_uuid = uuid.UUID(hash_value) + + return str(generated_uuid) diff --git a/src/device/service/drivers/oc_driver/__init__.py b/src/device/service/drivers/oc_driver/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612 --- /dev/null +++ b/src/device/service/drivers/oc_driver/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/device/service/drivers/oc_driver/templates/Interfaces/__init__.py b/src/device/service/drivers/oc_driver/templates/Interfaces/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..355dcdb04bdddd352966a9567a7a63117666e619 --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/Interfaces/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/device/service/drivers/oc_driver/templates/Interfaces/interfaces.py b/src/device/service/drivers/oc_driver/templates/Interfaces/interfaces.py new file mode 100644 index 0000000000000000000000000000000000000000..b4df7c2bff7b050e665de9afa5b44a7c6d85db18 --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/Interfaces/interfaces.py @@ -0,0 +1,43 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from yattag import Doc, indent +import logging +def interface_template (interface_data:dict) : + data={"name":"eth0","ip":"192.168.1.1","prefix-length":'24'} + doc, tag, text = Doc().tagtext() + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('interfaces', xmlns="http://openconfig.net/yang/interfaces"): + with tag('interface'): + with tag('name'):text(interface_data['name']) + with tag('config'): + with tag('name'):text(interface_data['name']) + with tag("enabled"):text(interface_data["enabled"]) + with tag('ipv4',xmlns="http://openconfig.net/yang/interfaces/ip") : + with tag("addresses"): + with tag("address"): + with tag('ip'):text(interface_data["ip"]) + with tag('config'): + with tag('ip'):text(interface_data["ip"]) + with tag('prefix-length'):text(interface_data["prefix-length"]) + + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '\r\n' + ) + logging.info("interfaces %s",result) + return result + + \ No newline at end of file diff --git a/src/device/service/drivers/oc_driver/templates/Tools.py b/src/device/service/drivers/oc_driver/templates/Tools.py new file mode 100644 index 0000000000000000000000000000000000000000..909bdd83bf4c189da0a778268ce40119cca7c452 --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/Tools.py @@ -0,0 +1,257 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re,logging +import json +import lxml.etree as ET +from typing import Collection, Dict, Any + +from yattag import Doc, indent +from .VPN.physical import create_optical_channel +def add_value_from_tag(target : Dict, field_name: str, field_value : ET.Element, cast=None) -> None: + if isinstance(field_value,str) or field_value is None or field_value.text is None: return + field_value = field_value.text + if cast is not None: field_value = cast(field_value) + target[field_name] = field_value + +def add_value_from_collection(target : Dict, field_name: str, field_value : Collection) -> None: + if field_value is None or len(field_value) == 0: return + target[field_name] = field_value + +""" +# Method Name: generate_templates + +# Parameters: + - resource_key: [str] Variable to identify the rule to be executed. + - resource_value: [str] Variable with the configuration parameters of the rule to be executed. + - delete: [bool] Variable to identify whether to create or delete the rule. + - vendor: [str] Variable to identify the vendor of the equipment to be configured. + +# Functionality: + This method generates the template to configure the equipment using pyangbind. + To generate the template the following steps are performed: + 1) Get the first parameter of the variable "resource_key" to identify the main path of the rule. + 2) Search for the specific configuration path + 3) Call the method with the configuration parameters (resource_data variable). + +# Return: + [dict] Set of templates generated according to the configuration rule +""" +def generate_templates(resource_key: str, resource_value: str, channel:str) -> str: # template management to be configured + + result_templates = [] + data={} + data['name']=channel + data['resource_key']=resource_key + data['value']=resource_value + result_templates.append(create_physical_config(data)) + + return result_templates +def extract_channel_xmlns (data_xml:str,is_opticalband:bool): + xml_bytes = data_xml.encode("utf-8") + root = ET.fromstring(xml_bytes) + + namespace=None + channels=None + + if (not is_opticalband) : + + optical_channel_namespaces = { + 'ns': 'urn:ietf:params:xml:ns:netconf:base:1.0', + 'oc': 'http://openconfig.net/yang/platform', + } + + channels= root.find('.//{*}optical-channel',optical_channel_namespaces) + if channels is not None : + optical_channel_namespace = channels.tag.replace("optical-channel", "") + namespace=optical_channel_namespace.replace("{", "").replace("}", "") + else : + optical_band_namespaces= { + 'oc':'http://openconfig.net/yang/wavelength-router' + } + + channels= root.find('.//{*}optical-bands',optical_band_namespaces) + if channels is not None: + optical_channel_namespace = channels.tag.replace("optical-bands", "") + namespace=optical_channel_namespace.replace("{", "").replace("}", "") + + + return namespace +def extract_channels_based_on_channelnamespace (xml_data:str,channel_namespace:str,is_opticalband:bool): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + channels=[] + + # Find the component names whose children include the "optical-channel" element + if (not is_opticalband): + namespace = {'namespace': 'http://openconfig.net/yang/platform','cn':channel_namespace} + + component_names = root.findall('.//namespace:component[cn:optical-channel]',namespace) + + # Extract and print the component names + for component in component_names: + component_name = component.find('namespace:name', namespace).text + channels.append({"index":component_name}) + else : + namespaces = { + 'wr': 'http://openconfig.net/yang/wavelength-router', + 'fs': channel_namespace + } + + wl = root.findall('.//fs:optical-band',namespaces=namespaces) + + for component in wl : + index=component.find('.//fs:index',namespaces).text + dest_port_name = component.find('.//fs:dest/fs:config/fs:port-name', namespaces).text + + # Retrieve port-name for source (assuming it exists in the XML structure) + source_port_name = component.find('.//fs:source/fs:config/fs:port-name', namespaces).text + channels.append({"index":index,"endpoints":(source_port_name,dest_port_name)}) + + # Retrieve port-name for dest + + return channels +def extract_channels_based_on_type (xml_data:str): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + + namespace = {'oc': 'http://openconfig.net/yang/platform', 'typex': 'http://openconfig.net/yang/platform-types'} + channel_names = [] + components = root.findall('.//oc:component', namespace) + for component in components: + + type_element = component.find('.//oc:state/oc:type[.="oc-opt-types:OPTICAL_CHANNEL"]',namespaces=namespace) + + if type_element is not None and type_element.text == 'oc-opt-types:OPTICAL_CHANNEL': + name_element = component.find('oc:name', namespace) + if name_element is not None: + channel_names.append(name_element.text) + return channel_names + +def extract_value(resource_key:str,xml_data:str,dic:dict,channel_name:str,channel_namespace:str): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + + namespace = {'oc': 'http://openconfig.net/yang/platform', + 'td': channel_namespace} + + element = root.find(f'.//oc:component[oc:name="{channel_name}"]', namespace) + + if element is not None: + parameter= element.find(f'.//td:{resource_key}',namespace) + if (parameter is not None): + value = parameter.text + dic[resource_key]=value + + else: + print(" element not found.") + + return dic + + +def extract_port_value (xml_string:list,port_name:str): + + xml_bytes = xml_string.encode("utf-8") + root = ET.fromstring(xml_bytes) + + namespace = {"oc": "http://openconfig.net/yang/platform"} + component=root.find(f".//oc:component[oc:name='{port_name}']", namespace) + onos_index = component.find( + f".//oc:property//oc:state/oc:name[.='onos-index']/../oc:value", namespace + ).text + + return (port_name,onos_index) + + + + +def extract_tranceiver (data_xml:str,dic:dict): + xml_bytes = data_xml.encode("utf-8") + root = ET.fromstring(xml_bytes) + namespaces = { + 'ns': 'urn:ietf:params:xml:ns:netconf:base:1.0', + 'oc': 'http://openconfig.net/yang/platform', + 'oc-terminal': 'http://openconfig.net/yang/terminal-device', + 'oc-platform-types': 'http://openconfig.net/yang/platform-types' + } + + + transceiver_components = root.findall('.//oc:component/oc:state/[oc:type="oc-platform-types:TRANSCEIVER"]../oc:state/oc:name', namespaces) + + component_names = [component.text for component in transceiver_components] + dic['transceiver']=component_names + return dic +def extract_interface (xml_data:str,dic:dict): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + namespaces = { + 'ns': 'urn:ietf:params:xml:ns:netconf:base:1.0', + 'oc': 'http://openconfig.net/yang/interfaces', + } + ip_namespaces = { + 'oc': 'http://openconfig.net/yang/interfaces', + 'ip': 'http://openconfig.net/yang/interfaces/ip', + } + + interfaces = root.findall('.//oc:interfaces/oc:interface', namespaces) + interface_names = [interface.find('oc:name', namespaces).text for interface in interfaces] + interface_enabled=[interface.find('oc:config/oc:enabled', namespaces).text for interface in interfaces] + ip_address_element = root.find('.//ip:ip', ip_namespaces) + interface_prefix_length=root.find('.//ip:prefix-length',ip_namespaces) + if (len(interface_names) > 0): + dic['interface']={"name":interface_names[0],'ip':ip_address_element.text,'enabled':interface_enabled[0],"prefix-length":interface_prefix_length.text} + else : + dic['interface']={} + return dic +def has_opticalbands(xml_data:str): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + + has_opticalbands=False + elements= root.find('.//{*}optical-bands') + + if (elements is not None and len(elements) >0): + has_opticalbands=True + else : + has_opticalbands=False + return has_opticalbands + +def extractor(data_xml:str,resource_keys:list,dic:dict): + + endpoints=[] + is_opticalband=has_opticalbands(xml_data=data_xml) + + channel_namespace=extract_channel_xmlns(data_xml=data_xml,is_opticalband=is_opticalband) + # channel_names=extract_channels_based_on_type(xml_data=data_xml) + # if len(channel_names)==0 : + channel_names= extract_channels_based_on_channelnamespace(xml_data=data_xml,channel_namespace=channel_namespace,is_opticalband=is_opticalband) + + lst_dic=[] + if (is_opticalband): + endpoints=channel_names + else: + + for channel_name in channel_names: + dic={} + for resource_key in resource_keys : + + if (resource_key != 'interface'): + dic=extract_value(dic=dic,resource_key=resource_key,xml_data=data_xml,channel_name=channel_name,channel_namespace=channel_namespace) + dic["name"]=channel_name + endpoints.append({"endpoint_uuid":{"uuid":channel_name}}) + lst_dic.append(dic) + transceivers_dic=extract_tranceiver(data_xml=data_xml,dic={}) + interfaces_dic=extract_interface(xml_data=data_xml,dic={}) + + return [transceivers_dic,interfaces_dic,lst_dic,channel_namespace,endpoints] \ No newline at end of file diff --git a/src/device/service/drivers/oc_driver/templates/VPN/__init__.py b/src/device/service/drivers/oc_driver/templates/VPN/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612 --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/VPN/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/device/service/drivers/oc_driver/templates/VPN/physical.py b/src/device/service/drivers/oc_driver/templates/VPN/physical.py new file mode 100644 index 0000000000000000000000000000000000000000..355858d2da891d96777d111f481c070fe3036d44 --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/VPN/physical.py @@ -0,0 +1,210 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from yattag import Doc, indent +import logging + +def seperate_port_config(resources:list,unwanted_keys:list[str])->list[list,dict,str]: + config=[] + ports={} + index=None + for item in resources : + + if (item['value'] is not None and (item['resource_key'] not in unwanted_keys)): + config.append({'resource_key':item['resource_key'], 'value':item['value']} ) + #if (item['resource_key'] == 'destination_port' or item['resource_key'] == 'source_port') and item['value'] is not None: + # ports[item['resource_key']]=item['value'] + if (item['resource_key'] == 'destination_port' or item['resource_key'] == 'source_port'): + ports[item['resource_key']]=item['value'] + if (item['resource_key']=='index' and item['value'] is not None) : + index=item['value'] + + return [config,ports,index] + + +def create_optical_channel(resources): + + unwanted_keys=['destination_port','source_port','channel_namespace','optical-band-parent','index', 'name'] + results =[] + data={"name":i["value"] for i in resources if i["resource_key"]=="channel_name"} + data["channel_namespace"]=next((i["value"] for i in resources if i["resource_key"] == "channel_namespace"), None) + config,ports,index=seperate_port_config(resources,unwanted_keys=unwanted_keys) + + port_val = "" + if 'destination_port' in ports and ports['destination_port'][0] is not None: + port_val = ports['destination_port'][0] + else: + port_val = ports['source_port'][0] + + + doc, tag, text = Doc().tagtext() + #with tag('config'): + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('components', xmlns="http://openconfig.net/yang/platform"): + with tag('component'): + with tag('name'):text("channel-{}".format(port_val)) + with tag('config'): + with tag('name'):text("channel-{}".format(port_val)) + with tag('optical-channel',xmlns=data["channel_namespace"]): + with tag('config'): + for resource in config: + with tag(resource['resource_key']):text(resource['value']) + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + + + return results + +def add_transceiver (transceiver_name:str): + + doc, tag, text = Doc().tagtext() + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('components', xmlns="http://openconfig.net/yang/platform"): + with tag('component'): + with tag('name'):text(transceiver_name) + with tag("config"): + with tag('name'):text(transceiver_name) + with tag("state"): + with tag('name'):text(transceiver_name) + with tag("type",('xmlns:oc-platform-types',"http://openconfig.net/yang/platform-types")):text("oc-platform-types:TRANSCEIVER") + with tag("transceiver",xmlns="http://openconfig.net/yang/platform/transceiver"): + with tag("config"): + with tag("enabled"):text("true") + with tag("form-factor-preconf",("xmlns:oc-opt-types","http://openconfig.net/yang/transport-types")):text("oc-opt-types:QSFP56_DD_TYPE1") + with tag("ethernet-pmd-preconf",("xmlns:oc-opt-types","http://openconfig.net/yang/transport-types")):text("oc-opt-types:ETH_400GBASE_ZR") + with tag("fec-mode",("xmlns:oc-platform-types","http://openconfig.net/yang/platform-types")):text("oc-platform-types:FEC_AUTO") + with tag("module-functional-type",("xmlns:oc-opt-types","http://openconfig.net/yang/transport-types")):text("oc-opt-types:TYPE_DIGITAL_COHERENT_OPTIC") + with tag("state"): + with tag("enabled"):text("true") + with tag("form-factor-preconf",("xmlns:oc-opt-types","http://openconfig.net/yang/transport-types")):text("oc-opt-types:QSFP56_DD_TYPE1") + with tag("ethernet-pmd-preconf",("xmlns:oc-opt-types","http://openconfig.net/yang/transport-types")):text("oc-opt-types:ETH_400GBASE_ZR") + with tag("fec-mode",("xmlns:oc-platform-types","http://openconfig.net/yang/platform-types")):text("oc-platform-types:FEC_AUTO") + with tag("module-functional-type",("xmlns:oc-opt-types","http://openconfig.net/yang/transport-types")):text("oc-opt-types:TYPE_DIGITAL_COHERENT_OPTIC") + with tag("vendor"):text("Cisco") + with tag("vendor-part"):text("400zr-QSFP-DD") + with tag("vendor-rev"):text("01") + with tag("serial-no"):text("1567321") + with tag("physical-channels"): + with tag("channel"): + with tag("index"):text("1") + with tag("config"): + with tag("index"):text("1") + with tag("associated-optical-channel"):text("channel-4") + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + + + return result + +def create_optical_band (resources) : + results =[] + unwanted_keys=['destination_port','source_port','channel_namespace','frequency','optical-band-parent'] + config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) + doc, tag, text = Doc().tagtext() + #with tag('config'): + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): + with tag('optical-bands',xmlns="http://flex-scale-project.eu/yang/flex-scale-mg-on"): + n = 0 + if 'destination_port' in ports: + n = len(ports['destination_port']) + else: + n = len(ports['source_port']) + for i in range(0, n): + #with tag('optical-band', operation="create"): + with tag('optical-band'): + if index is not None: + with tag('index'):text(str(int(index)+i)) + with tag('config'): + #if index is not None: + # with tag('index'):text(str(int(index)+i)) + for resource in config: + if resource['resource_key'] == "index": + with tag('index'):text(str(int(index)+i)) + else: + with tag(resource['resource_key']):text(resource['value']) + with tag('admin-status'):text('ENABLED') + #with tag('fiber-parent'):text(ports['destination_port'] if 'destination_port' in ports else ports['source_port']) + if ('destination_port' in ports) and (ports['destination_port'][i] is not None): + with tag('dest'): + with tag('config'): + with tag('port-name'):text(ports['destination_port'][i]) + if ('source_port' in ports) and (ports['source_port'][i] is not None): + with tag('source'): + with tag('config'): + with tag('port-name'):text(ports['source_port'][i]) + + + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + return results + +def create_media_channel (resources): + results=[] + unwanted_keys=['destination_port','source_port','channel_namespace','frequency','operational-mode', 'optical-band-parent'] + config,ports,index= seperate_port_config(resources,unwanted_keys=unwanted_keys) + doc, tag, text = Doc().tagtext() + #with tag('config'): + with tag('config',xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"): + with tag('wavelength-router', xmlns="http://openconfig.net/yang/wavelength-router"): + with tag('media-channels'): + n = 0 + if 'destination_port' in ports: + n = len(ports['destination_port']) + else: + n = len(ports['source_port']) + for i in range(0, n): + #with tag('channel', operation="create"): + with tag('channel'): + with tag('index'):text(str(int(index)+i)) + with tag('config'): + #with tag('index'):text(index) + for resource in config: + + if resource['resource_key'] == "index": + with tag('index'):text(str(int(index)+i)) + else: + with tag(resource['resource_key']):text(resource['value']) + if ('destination_port' in ports) and (ports['destination_port'][i] is not None): + with tag('dest'): + with tag('config'): + with tag('port-name'):text(ports['destination_port'][i]) + if ('source_port' in ports) and (ports['source_port'][i] is not None): + with tag('source'): + with tag('config'): + with tag('port-name'):text(ports['source_port'][i]) + + + result = indent( + doc.getvalue(), + indentation = ' '*2, + newline = '' + ) + results.append(result) + return results + + + \ No newline at end of file diff --git a/src/device/service/drivers/oc_driver/templates/__init__.py b/src/device/service/drivers/oc_driver/templates/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612 --- /dev/null +++ b/src/device/service/drivers/oc_driver/templates/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/opticalcontroller/Dockerfile b/src/opticalcontroller/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..c3d886ab56ecf42cd2ffb96b32ef59b89f2c1207 --- /dev/null +++ b/src/opticalcontroller/Dockerfile @@ -0,0 +1,79 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM python:3.9-slim + +# Install dependencies +RUN apt-get --yes --quiet --quiet update && \ + apt-get --yes --quiet --quiet install wget g++ && \ + rm -rf /var/lib/apt/lists/* + +# Set Python to show logs as they occur +ENV PYTHONUNBUFFERED=0 + +# Download the gRPC health probe +# RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \ +# wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ +# chmod +x /bin/grpc_health_probe + + + + +# Get generic Python packages +RUN python3 -m pip install --upgrade pip +RUN python3 -m pip install --upgrade setuptools wheel +RUN python3 -m pip install --upgrade pip-tools + + + +COPY common_requirements.in common_requirements.in +RUN pip-compile --quiet --output-file=common_requirements.txt common_requirements.in +RUN python3 -m pip install -r common_requirements.txt + +RUN mkdir -p /var/teraflow/opticalcontroller + +WORKDIR /var/teraflow/opticalcontroller/common +COPY src/common/. ./ +RUN rm -rf proto + + +# Create proto sub-folder, copy .proto files, and generate Python code +RUN mkdir -p /var/teraflow/opticalcontroller/common/proto +WORKDIR /var/teraflow/opticalcontroller/common/proto +RUN touch __init__.py +COPY proto/*.proto ./ +RUN python3 -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. *.proto +RUN rm *.proto +RUN find . -type f -exec sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' {} \; + +# Create component sub-folder, get specific Python packages + + + +WORKDIR /var/teraflow/opticalcontroller +COPY src/opticalcontroller/requirements.in requirements.in +RUN pip-compile --quiet --output-file=requirements.txt requirements.in +RUN python3 -m pip install -r requirements.txt + +# Add component files into working directory +WORKDIR /var/teraflow/ + +COPY src/context/. context/ + +COPY src/opticalcontroller/. opticalcontroller/ +COPY src/context/. opticalcontroller/context/ + +# Start the service +WORKDIR /var/teraflow/opticalcontroller +ENTRYPOINT ["python", "OpticalController.py"] diff --git a/src/opticalcontroller/OpticalController.py b/src/opticalcontroller/OpticalController.py new file mode 100644 index 0000000000000000000000000000000000000000..c2805695a75933c73d4ad367176bee8b504d4460 --- /dev/null +++ b/src/opticalcontroller/OpticalController.py @@ -0,0 +1,245 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from flask import Flask +from flask import render_template +from flask_restplus import Resource, Api + +from tools import * +from variables import * +from RSA import RSA +import time +import logging + + +rsa = None +LOGGER = logging.getLogger(__name__) + +app = Flask(__name__) +api = Api(app, version='1.0', title='Optical controller API', + description='Rest API to configure OC Optical devices in TFS') +# app.config.from_object('config') +# appbuilder = AppBuilder(app, indexview=MyIndexView) +optical = api.namespace('OpticalTFS', description='TFS Optical APIs') + + +@app.route('/index') +def index(): + return render_template('index.html') + + +#@optical.route('/AddLightpath/<string:src>/<string:dst>/<int:bitrate>/<int:bidir>') +@optical.route('/AddLightpath/<string:src>/<string:dst>/<int:bitrate>') +@optical.response(200, 'Success') +@optical.response(404, 'Error, not found') +class AddLightpath(Resource): + @staticmethod + def put(src, dst, bitrate, bidir=1): + + LOGGER.info("INFO: New Lightpath request from {} to {} with rate {} ".format(src, dst, bitrate)) + t0 = time.time()*1000.0 + if debug: + rsa.g.printGraph() + + if rsa is not None: + flow_id = rsa.rsa_computation(src, dst, bitrate, bidir) + if rsa.db_flows[flow_id]["op-mode"] == 0: + return 'No path found', 404 + t1 = time.time()*1000.0 + elapsed = t1 - t0 + LOGGER.info("INFO: time elapsed = {} ms".format(elapsed)) + return rsa.db_flows[flow_id], 200 + else: + return "Error", 404 + + +#@optical.route('/AddFlexLightpath/<string:src>/<string:dst>/<int:bitrate>') +@optical.route('/AddFlexLightpath/<string:src>/<string:dst>/<int:bitrate>', + defaults={"bidir": 1, "band": None}) +@optical.route('/AddFlexLightpath/<string:src>/<string:dst>/<int:bitrate>/<int:bidir>', + defaults={"band": None}) +@optical.route('/AddFlexLightpath/<string:src>/<string:dst>/<int:bitrate>/<int:bidir>/<int:band>',) +@optical.response(200, 'Success') +@optical.response(404, 'Error, not found') +class AddFlexLightpath(Resource): + @staticmethod + def put(src, dst, bitrate,bidir=1, band=None): + + print("INFO: New FlexLightpath request from {} to {} with rate {} ".format(src, dst, bitrate)) + LOGGER.info("INFO: New FlexLightpath request from {} to {} with rate {} ".format(src, dst, bitrate)) + t0 = time.time()*1000.0 + if debug: + rsa.g.printGraph() + + if rsa is not None: + flow_id, optical_band_id = rsa.rsa_fs_computation(src, dst, bitrate, bidir, band) + print (f"flow_id {flow_id} and optical_band_id {optical_band_id} ") + if flow_id is not None: + if rsa.db_flows[flow_id]["op-mode"] == 0: + return 'No path found', 404 + t1 = time.time() * 1000.0 + elapsed = t1 - t0 + print("INFO: time elapsed = {} ms".format(elapsed)) + + return rsa.db_flows[flow_id], 200 + else: + if len(rsa.optical_bands[optical_band_id]["flows"]) == 0: + return 'No path found', 404 + else: + t1 = time.time() * 1000.0 + elapsed = t1 - t0 + LOGGER.info("INFO: time elapsed = {} ms".format(elapsed)) + + return rsa.optical_bands[optical_band_id], 200 + else: + return "Error", 404 + +@optical.route('/DelFlexLightpath/<int:flow_id>/<string:src>/<string:dst>/<int:bitrate>/<int:o_band_id>') +@optical.response(200, 'Success') +@optical.response(404, 'Error, not found') +class DelLightpath(Resource): + @staticmethod + def delete(flow_id, src, dst, bitrate, o_band_id): + if flow_id in rsa.db_flows.keys(): + flow = rsa.db_flows[flow_id] + bidir = flow["bidir"] + match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] == bitrate + if bidir: + match2 = flow["src"] == dst and flow["dst"] == src and flow["bitrate"] == bitrate + if match1 or match2: + ob_id = flow["parent_opt_band"] + rsa.del_flow(flow, ob_id) + rsa.db_flows[flow_id]["is_active"] = False + rsa.optical_bands[ob_id]["served_lightpaths"].remove(flow_id) + if rsa.optical_bands[ob_id]["reverse_optical_band_id"] != 0: + rev_ob_id = rsa.optical_bands[ob_id]["reverse_optical_band_id"] + rsa.optical_bands[rev_ob_id]["served_lightpaths"].remove(flow_id) + + if debug: + LOGGER.info(links_dict) + return "flow {} deleted".format(flow_id), 200 + else: + return "flow {} not matching".format(flow_id), 404 + else: + if match1: + ob_id = flow["parent_opt_band"] + rsa.del_flow(flow, ob_id) + rsa.db_flows[flow_id]["is_active"] = False + rsa.optical_bands[ob_id]["served_lightpaths"].remove(flow_id) + if debug: + LOGGER.info(links_dict) + return "flow {} deleted".format(flow_id), 200 + else: + return "flow {} not matching".format(flow_id), 404 + else: + return "flow id {} does not exist".format(flow_id), 404 + + + +@optical.route('/DelLightpath/<int:flow_id>/<string:src>/<string:dst>/<int:bitrate>') +@optical.response(200, 'Success') +@optical.response(404, 'Error, not found') +class DelLightpath(Resource): + @staticmethod + def delete(flow_id, src, dst, bitrate): + if flow_id in rsa.db_flows.keys(): + flow = rsa.db_flows[flow_id] + match1 = flow["src"] == src and flow["dst"] == dst and flow["bitrate"] == bitrate + match2 = flow["src"] == dst and flow["dst"] == src and flow["bitrate"] == bitrate + if match1 or match2: + rsa.del_flow(flow) + rsa.db_flows[flow_id]["is_active"] = False + if debug: + LOGGER.info(links_dict) + return "flow {} deleted".format(flow_id), 200 + else: + return "flow {} not matching".format(flow_id), 404 + else: + return "flow id {} does not exist".format(flow_id), 404 + + +@optical.route('/GetLightpaths') +@optical.response(200, 'Success') +@optical.response(404, 'Error, not found') +class GetFlows(Resource): + @staticmethod + def get(): + try: + if debug: + LOGGER.info(rsa.db_flows) + return rsa.db_flows, 200 + except: + return "Error", 404 + +@optical.route('/GetOpticalBands') +@optical.response(200, 'Success') +@optical.response(404, 'Error, not found') +class GetBands(Resource): + @staticmethod + def get(): + print("Getting ") + LOGGER.info("Getting") + try: + if debug: + LOGGER.info(rsa.optical_bands) + return rsa.optical_bands, 200 + except: + return "Error", 404 + + +@optical.route('/GetOpticalBand/<int:ob_id>') +@optical.response(200, 'Success') +@optical.response(404, 'Error, not found') +class GetBand(Resource): + @staticmethod + def get(ob_id): + for ob_idx in rsa.optical_bands.keys(): + if str(ob_idx) == str(ob_id): + if debug: + LOGGER.info(rsa.optical_bands[ob_id]) + return rsa.optical_bands[ob_idx], 200 + return {}, 404 + + +@optical.route('/GetLinks') +@optical.response(200, 'Success') +@optical.response(404, 'Error, not found') +class GetFlows(Resource): + @staticmethod + def get(): + global links_dict + try: + if debug: + LOGGER.info(links_dict) + return links_dict, 200 + except: + return "Error", 404 + + +if __name__ == '__main__': + + + # Start metrics server + + LOGGER.info('Starting...') + + + + nodes_dict, links_dict = readTopologyData(nodes_json, topology_json) + + #topologies, links = getTopology() + #print("topologies{} and devices {}".format(topologies,links)) + rsa = RSA(nodes_dict, links_dict) + + app.run(host='0.0.0.0', port=10060, debug=True) diff --git a/src/opticalcontroller/README.md b/src/opticalcontroller/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5fd94c59e51cbd78dd76a7db0f24aaaec4ebd9db --- /dev/null +++ b/src/opticalcontroller/README.md @@ -0,0 +1,17 @@ +# optical-controller +This a framework to implement the optical controller for the RMSA algorithm. +#create a venv +python -m venv venv + +in linux +source venv/Scripts/activate + +in windows +venv\Scripts\activate + +pip install -r requirements_opt.txt + +python OpticalController.py + + + diff --git a/src/opticalcontroller/RSA.py b/src/opticalcontroller/RSA.py new file mode 100644 index 0000000000000000000000000000000000000000..9b12b1ac8d9302c2bb622b2d4b81924b5453036c --- /dev/null +++ b/src/opticalcontroller/RSA.py @@ -0,0 +1,931 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import dijsktra +from tools import * +from variables import * + + +class RSA(): + def __init__(self, nodes, links): + self.nodes_dict = nodes + self.links_dict = links + self.g = None + + self.flow_id = 0 + self.opt_band_id = 0 + self.db_flows = {} + self.initGraph() + self.c_slot_number = 0 + self.l_slot_number = 0 + self.s_slot_number = 0 + self.optical_bands = {} + + def init_link_slots(self, testing): + if not testing: + for l in self.links_dict["links"]: + for fib in l["optical_link"]["details"]["fibers"]: + #fib = self.links_dict[l]["fibers"][f] + if len(fib["c_slots"]) > 0: + fib["c_slots"] = list(range(0, Nc)) + if len(fib["l_slots"]) > 0: + fib["l_slots"] = list(range(0, Nl)) + if len(fib["s_slots"]) > 0: + fib["s_slots"] = list(range(0, Ns)) + if debug: + print(fib) + for l1 in self.links_dict["links"]: + + for fib1 in l1["optical_link"]["details"]["fibers"]: + #fib1 = self.links_dict[l1]["details"]["fibers"][f1] + + self.c_slot_number = len(fib1["c_slots"]) + self.l_slot_number = len(fib1["l_slots"]) + self.s_slot_number = len(fib1["s_slots"]) + + break + break + return "{},{},{}".format(self.c_slot_number, self.l_slot_number, self.s_slot_number) + + def initGraph(self): + self.g = dijsktra.Graph() + for n in self.nodes_dict: + self.g.add_vertex(n) + for l in self.links_dict["links"]: + if debug: + print(l) + [s, d] = l["optical_link"]["name"].split('-') + ps = l["optical_link"]["details"]["source"] + pd = l["optical_link"]["details"]["target"] + self.g.add_edge(s, d, ps, pd, 1) + + print("INFO: Graph initiated.") + if debug: + self.g.printGraph() + + def compute_path(self, src, dst): + path = dijsktra.shortest_path(self.g, self.g.get_vertex(src), self.g.get_vertex(dst)) + print("INFO: Path from {} to {} with distance: {}".format(src, dst, self.g.get_vertex(dst).get_distance())) + if debug: + print(path) + links = [] + for i in range(0, len(path) - 1): + s = path[i] + if debug: + print(s) + if i < len(path) - 1: + d = path[i + 1] + link_id = "{}-{}".format(s, d) + if debug: + #print(link_id, self.links_dict[link_id]) + print(link_id, self.get_link_by_name(link_id)) + + links.append(link_id) + self.g.reset_graph() + return links, path + + def get_slots(self, links, slots, optical_band_id = None): + + if isinstance(slots, int): + val_c = slots + val_s = slots + val_l = slots + else: + val_c = self.c_slot_number + val_l = self.l_slot_number + val_s = self.s_slot_number + + c_sts = [] + l_sts = [] + s_sts = [] + c_slots = {} + l_slots = {} + s_slots = {} + add = "" + drop = "" + src_1, dst_1 = links[0].split('-') + src_2, dst_2 = links[-1].split('-') + if self.nodes_dict[src_1]["type"] == "OC-TP": + add = links[0] + if self.nodes_dict[dst_2]["type"] == "OC-TP": + drop = links[-1] + + for l in links: + c_slots[l] = [] + l_slots[l] = [] + s_slots[l] = [] + found = 0 + for link in self.links_dict["links"]: + if link["optical_link"]["name"] == l: + #for f in self.links_dict[l]['fibers'].keys(): + for fib in link["optical_link"]["details"]["fibers"]: + if l == add: + if 'used' in fib: + if fib["used"]: + #if debug: + print("WARNING!!!: link {}, fiber {} is already in use".format(l, fib["ID"])) + continue + if l == drop: + if 'used' in fib: + if fib["used"]: + #if debug: + print("WARNING!!!: link {}, fiber {} is already in use".format(l, fib["ID"])) + continue + if len(fib["c_slots"]) > 0: + c_slots[l] = combine(c_slots[l], consecutives(fib["c_slots"], val_c)) + if len(fib["l_slots"]) > 0: + l_slots[l] = combine(l_slots[l], consecutives(fib["l_slots"], val_l)) + if len(fib["s_slots"]) > 0: + s_slots[l] = combine(s_slots[l], consecutives(fib["s_slots"], val_s)) + if debug: + print(l, c_slots[l]) + found = 1 + if found == 0: + return [], [], [] + + keys = list(c_slots.keys()) + if debug: + print(len(keys)) + if debug: + print(keys[0]) + # intersection among the slots over all links + if len(keys) == 1: + c_sts = c_slots[keys[0]] + l_sts = l_slots[keys[0]] + s_sts = s_slots[keys[0]] + else: + for i in range(1, len(keys)): + if debug: + print(keys[i]) + # set a for the intersection + if i == 1: + a_c = c_slots[keys[i - 1]] + a_l = l_slots[keys[i - 1]] + a_s = s_slots[keys[i - 1]] + else: + a_c = c_sts + a_l = l_sts + a_s = s_sts + # set b for the intersection + b_c = c_slots[keys[i]] + b_l = l_slots[keys[i]] + b_s = s_slots[keys[i]] + + c_sts = common_slots(a_c, b_c) + l_sts = common_slots(a_l, b_l) + s_sts = common_slots(a_s, b_s) + if optical_band_id is not None: + if "c_slots" in self.optical_bands[optical_band_id].keys(): + if len(self.optical_bands[optical_band_id]["c_slots"]) > 0: + a_c = c_sts + b_c = self.optical_bands[optical_band_id]["c_slots"] + c_sts = common_slots(a_c, b_c) + else: + c_sts = [] + else: + c_sts = [] + if "l_slots" in self.optical_bands[optical_band_id].keys(): + if len(self.optical_bands[optical_band_id]["l_slots"]) > 0: + a_l = l_sts + b_l = self.optical_bands[optical_band_id]["l_slots"] + l_sts = common_slots(a_l, b_l) + else: + l_sts = [] + else: + l_sts = [] + if "s_slots" in self.optical_bands[optical_band_id].keys(): + if len(self.optical_bands[optical_band_id]["s_slots"]) > 0: + a_s = s_sts + b_s = self.optical_bands[optical_band_id]["s_slots"] + s_sts = common_slots(a_s, b_s) + else: + s_sts = [] + else: + s_sts = [] + + return c_sts, l_sts, s_sts + + def update_link(self, fib, slots, band): + for i in slots: + fib[band].remove(i) + if 'used' in fib: + fib['used'] = True + + def update_optical_band(self, optical_band_id, slots, band): + for i in slots: + self.optical_bands[optical_band_id][band].remove(i) + + def restore_link(self, fib, slots, band): + for i in slots: + fib[band].append(int(i)) + if 'used' in fib: + fib['used'] = False + fib[band].sort() + + def restore_optical_band(self, optical_band_id, slots, band): + for i in slots: + self.optical_bands[optical_band_id][band].append(int(i)) + self.optical_bands[optical_band_id][band].sort() + + def del_flow(self, flow, o_b_id = None): + flows = flow["flows"] + band = flow["band_type"] + slots = flow["slots"] + fiber_f = flow["fiber_forward"] + fiber_b = flow["fiber_backward"] + op = flow["op-mode"] + n_slots = flow["n_slots"] + path = flow["path"] + links = flow["links"] + bidir = flow["bidir"] + + for l in fiber_f.keys(): + if debug: + print(l) + print(fiber_f[l]) + #link = self.links_dict[l] + #f = fiber_f[l] + #fib = link['fibers'][f] + fib = self.get_fiber_details(l, fiber_f[l]) + if not list_in_list(slots, fib[band]): + self.restore_link(fib, slots, band) + if debug: + print(fib[band]) + if o_b_id is not None: + self.restore_optical_band(o_b_id, slots, band) + if bidir: + for rl in fiber_b.keys(): + if debug: + print(rl) + print(fiber_b[rl]) + #rlink = self.links_dict[rl] + #rf = fiber_b[rl] + #rfib = rlink['fibers'][rf] + rfib = self.get_fiber_details(rl, fiber_b[rl]) + if not list_in_list(slots, rfib[band]): + self.restore_link(rfib, slots, band) + if debug: + print(rfib[band]) + #changed according to TFS development + #if o_b_id is not None: + # rev_o_band_id = self.optical_bands[o_b_id]["reverse_optical_band_id"] + # self.restore_optical_band(rev_o_band_id, slots, band) + return True + + def get_fibers_forward(self, links, slots, band): + fiber_list = {} + add = links[0] + drop = links[-1] + print(links) + ''' + for link in self.links_dict["links"]: + if link["optical_link"]["name"] == l: + # for f in self.links_dict[l]['fibers'].keys(): + for fib in link["optical_link"]["details"]["fibers"]: + + ''' + for l in links: + for link in self.links_dict["links"]: + if link["optical_link"]["name"] == l: + for fib in link["optical_link"]["details"]["fibers"]: + #for f in self.links_dict[l]['fibers'].keys(): + #for fib in l["optical_link"]["details"]["fibers"]: + #fib = self.links_dict[l]['fibers'][f] + if l == add: + if 'used' in fib: + if fib["used"]: + if debug: + print("link {}, fiber {} is already in use".format(l, fib["ID"])) + continue + if l == drop: + if 'used' in fib: + if fib["used"]: + if debug: + print("link {}, fiber {} is already in use".format(l, fib["ID"])) + continue + if list_in_list(slots, fib[band]): + fiber_list[l] = fib["ID"] + self.update_link(fib, slots, band) + break + print("INFO: Path forward computation completed") + return fiber_list + + def get_link_by_name (self, key): + result = None + for link in self.links_dict["links"]: + if link["optical_link"]["name"] == key: + if debug: + print(link) + result = link + break + return result + + def get_fiber_details(self, link_key, fiber_id): + for link in self.links_dict["links"]: + if link["optical_link"]["name"] == link_key: + if debug: + print(link) + for fib in link["optical_link"]["details"]["fibers"]: + if fib["ID"] == fiber_id: + return fib + return None + + + def get_fibers_backward(self, links, fibers, slots, band): + fiber_list = {} + #r_drop = reverse_link(links[0]) + #r_add = reverse_link(links[-1]) + for l in fibers.keys(): + fib = self.get_fiber_details(l, fibers[l]) + ''' + link = self.get_link_by_name(l) + #port = self.links_dict[l]["fibers"][fibers[l]]["src_port"] + for fib in link["optical_link"]["details"]["fibers"]: + if fib["ID"] == fibers[l]: + ''' + port = fib["src_port"] + r_l = reverse_link(l) + r_link = self.get_link_by_name(r_l) + #for f in r_link["fibers"].keys(): + for r_fib in r_link["optical_link"]["details"]["fibers"]: + if r_fib["remote_peer_port"] == port: + if list_in_list(slots, r_fib[band]): + fiber_list[r_l] = r_fib["ID"] + self.update_link(r_fib, slots, band) + print("INFO: Path backward computation completed") + return fiber_list + + def select_slots_and_ports(self, links, n_slots, c, l, s, bidir): + if debug: + print(self.links_dict) + band, slots = slot_selection(c, l, s, n_slots, self.c_slot_number, self.l_slot_number, self.s_slot_number) + if band is None: + print("No slots available in the three bands") + return None, None, None + if debug: + print(band, slots) + fibers_f = self.get_fibers_forward(links, slots, band) + + fibers_b = [] + if bidir: + fibers_b = self.get_fibers_backward(links, fibers_f, slots, band) + if debug: + print("forward") + print(fibers_f) + print("backward") + print(fibers_b) + add = links[0] + drop = links[-1] + inport = "0" + outport = "0" + r_inport = "0" + r_outport = "0" + t_flows = {} + #if len(links) == 1: + + for lx in fibers_f: + if lx == add: + inport = "0" + r_outport = "0" + if lx == drop: + outport = "0" + r_inport = "0" + f = fibers_f[lx] + src, dst = lx.split("-") + fibx = self.get_fiber_details(lx, f) + #outport = self.links_dict[lx]['fibers'][f]["src_port"] + outport = fibx["src_port"] + + t_flows[src] = {} + t_flows[src]["f"] = {} + t_flows[src]["b"] = {} + t_flows[src]["f"] = {"in": inport, "out": outport} + + if bidir: + #r_inport = self.links_dict[lx]['fibers'][f]["local_peer_port"] + r_inport = fibx["local_peer_port"] + t_flows[src]["b"] = {"in": r_inport, "out": r_outport} + + #inport = self.links_dict[lx]['fibers'][f]["dst_port"] + inport = fibx["dst_port"] + if bidir: + #r_outport = self.links_dict[lx]['fibers'][f]["remote_peer_port"] + r_outport = fibx["remote_peer_port"] + t_flows[dst] = {} + t_flows[dst]["f"] = {} + t_flows[dst]["b"] = {} + t_flows[dst]["f"] = {"in": inport, "out": "0"} + if bidir: + t_flows[dst]["b"] = {"in": "0", "out": r_outport} + + if debug: + print(self.links_dict) + + if debug: + print(t_flows) + print("INFO: Flow matrix computed") + + return t_flows, band, slots, fibers_f, fibers_b + + def select_slots_and_ports_fs(self, links, n_slots, c, l, s, bidir, o_band_id): + if debug: + print(self.links_dict) + band, slots = slot_selection(c, l, s, n_slots, self.c_slot_number, self.l_slot_number, self.s_slot_number) + if band is None: + print("No slots available in the three bands") + return None, None, None, None, None + if debug: + print(band, slots) + fibers_f = self.get_fibers_forward(links, slots, band) + self.update_optical_band(o_band_id, slots, band) + fibers_b = [] + if bidir: + fibers_b = self.get_fibers_backward(links, fibers_f, slots, band) + ''' + + rev_o_band_id = self.optical_bands[o_band_id]["reverse_optical_band_id"] + self.update_optical_band(rev_o_band_id, slots, band) + ''' + if debug: + print("forward") + print(fibers_f) + if bidir: + print("backward") + print(fibers_b) + add = links[0] + drop = links[-1] + port_0 = "0" + + t_flows = {} + + #flows_add_side + f = fibers_f[add] + src, dst = add.split("-") + fibx = self.get_fiber_details(add, f) + #outport = self.links_dict[add]['fibers'][f]["src_port"] + outport = fibx["src_port"] + #T1 rules + t_flows[src] = {} + t_flows[src]["f"] = {} + t_flows[src]["b"] = {} + t_flows[src]["f"] = {"in": port_0, "out": outport} + if bidir: + #r_inport = self.links_dict[add]['fibers'][f]["local_peer_port"] + r_inport = fibx["local_peer_port"] + t_flows[src]["b"] = {"in": r_inport, "out": port_0} + + #R1 rules + t_flows[dst] = {} + t_flows[dst]["f"] = {} + t_flows[dst]["b"] = {} + #inport = self.links_dict[add]['fibers'][f]["dst_port"] + inport = fibx["dst_port"] + opt_band_src_port = self.optical_bands[o_band_id]["src_port"] + t_flows[dst]["f"] = {"in": inport, "out": opt_band_src_port} + #to modify to peer ports + if bidir: + #r_inport = self.links_dict[add]['fibers'][f]["local_peer_port"] + r_inport = fibx["local_peer_port"] + t_flows[src]["b"] = {"in": r_inport, "out": port_0} + if bidir: + rev_opt_band_dst_port = self.optical_bands[o_band_id]["rev_dst_port"] + #r_outport = self.links_dict[add]['fibers'][f]["remote_peer_port"] + r_outport = fibx["remote_peer_port"] + t_flows[dst]["b"] = {"in": rev_opt_band_dst_port, "out": r_outport} + + #flows_drop_side + # R2 rules + f = fibers_f[drop] + src, dst = drop.split("-") + fiby = self.get_fiber_details(drop, f) + #outport = self.links_dict[drop]['fibers'][f]["src_port"] + outport = fiby["src_port"] + + t_flows[src] = {} + t_flows[src]["f"] = {} + t_flows[src]["b"] = {} + opt_band_dst_port = self.optical_bands[o_band_id]["dst_port"] + t_flows[src]["f"] = {"in": opt_band_dst_port, "out": outport} + if bidir: + rev_opt_band_src_port = self.optical_bands[o_band_id]["rev_src_port"] + #r_inport = self.links_dict[drop]['fibers'][f]["local_peer_port"] + r_inport = fiby["local_peer_port"] + t_flows[src]["b"] = {"in": r_inport, "out": rev_opt_band_src_port} + t_flows[dst] = {} + t_flows[dst]["f"] = {} + t_flows[dst]["b"] = {} + #inport = self.links_dict[drop]['fibers'][f]["dst_port"] + inport = fiby["dst_port"] + t_flows[dst]["f"] = {"in": inport, "out": port_0} + if bidir: + #r_inport = self.links_dict[drop]['fibers'][f]["remote_peer_port"] + r_inport = fiby["remote_peer_port"] + t_flows[dst]["b"] = {"in": port_0, "out": r_inport} + + if debug: + print(self.links_dict) + + if debug: + print(t_flows) + print("INFO: Flow matrix computed for Flex Lightpath") + + return t_flows, band, slots, fibers_f, fibers_b + + def rsa_computation(self, src, dst, rate, bidir): + self.flow_id += 1 + self.db_flows[self.flow_id] = {} + self.db_flows[self.flow_id]["flow_id"] = self.flow_id + self.db_flows[self.flow_id]["src"] = src + self.db_flows[self.flow_id]["dst"] = dst + self.db_flows[self.flow_id]["bitrate"] = rate + self.db_flows[self.flow_id]["bidir"] = bidir + + links, path = self.compute_path(src, dst) + + if len(path) < 1: + self.null_values(self.flow_id) + return self.flow_id + op, num_slots = map_rate_to_slot(rate) + c_slots, l_slots, s_slots = self.get_slots(links, num_slots) + if debug: + print(c_slots) + print(l_slots) + print(s_slots) + if len(c_slots) > 0 or len(l_slots) > 0 or len(s_slots) > 0: + flow_list, band_range, slots, fiber_f, fiber_b = self.select_slots_and_ports(links, num_slots, c_slots, + l_slots, s_slots, bidir) + f0, band = freqency_converter(band_range, slots) + if debug: + print(f0, band) + print("INFO: RSA completed for normal wavelenght connection") + if flow_list is None: + self.null_values(self.flow_id) + return self.flow_id + slots_i = [] + for i in slots: + slots_i.append(int(i)) + # return links, path, flow_list, band_range, slots, fiber_f, fiber_b, op, num_slots, f0, band + # links, path, flows, bx, slots, fiber_f, fiber_b, op, n_slots, f0, band + self.db_flows[self.flow_id]["flows"] = flow_list + self.db_flows[self.flow_id]["band_type"] = band_range + self.db_flows[self.flow_id]["slots"] = slots_i + self.db_flows[self.flow_id]["fiber_forward"] = fiber_f + self.db_flows[self.flow_id]["fiber_backward"] = fiber_b + self.db_flows[self.flow_id]["op-mode"] = op + self.db_flows[self.flow_id]["n_slots"] = num_slots + self.db_flows[self.flow_id]["links"] = links + self.db_flows[self.flow_id]["path"] = path + self.db_flows[self.flow_id]["band"] = band + self.db_flows[self.flow_id]["freq"] = f0 + self.db_flows[self.flow_id]["is_active"] = True + return self.flow_id + + def null_values(self, flow_id): + self.db_flows[flow_id]["flows"] = {} + self.db_flows[flow_id]["band_type"] = "" + self.db_flows[flow_id]["slots"] = [] + self.db_flows[flow_id]["fiber_forward"] = [] + self.db_flows[flow_id]["fiber_backward"] = [] + self.db_flows[flow_id]["op-mode"] = 0 + self.db_flows[flow_id]["n_slots"] = 0 + self.db_flows[flow_id]["links"] = {} + self.db_flows[flow_id]["path"] = [] + self.db_flows[flow_id]["band"] = 0 + self.db_flows[flow_id]["freq"] = 0 + self.db_flows[flow_id]["is_active"] = False + + def null_values_ob(self, ob_id): + self.optical_bands[ob_id]["flows"] = {} + self.optical_bands[ob_id]["band_type"] = "" + #self.optical_bands[ob_id]["slots"] = [] + self.optical_bands[ob_id]["fiber_forward"] = [] + self.optical_bands[ob_id]["n_slots"] = 0 + self.optical_bands[ob_id]["links"] = {} + self.optical_bands[ob_id]["path"] = [] + self.optical_bands[ob_id]["band"] = 0 + self.optical_bands[ob_id]["freq"] = 0 + self.optical_bands[ob_id]["is_active"] = False + self.optical_bands[ob_id]["c_slots"] = [] + self.optical_bands[ob_id]["l_slots"] = [] + self.optical_bands[ob_id]["s_slots"] = [] + self.optical_bands[ob_id]["served_lightpaths"] = [] + self.optical_bands[ob_id]["reverse_optical_band_id"] = 0 + self.db_flows[self.flow_id]["parent_opt_band"] = 0 + self.db_flows[self.flow_id]["new_optical_band"] = 0 + + def create_optical_band(self, links, path, bidir, num_slots): + print("INFO: Creating optical-band of {} slots".format(num_slots)) + self.opt_band_id += 1 + forw_opt_band_id = self.opt_band_id + self.optical_bands[forw_opt_band_id] = {} + self.optical_bands[forw_opt_band_id]["optical_band_id"] = forw_opt_band_id + self.optical_bands[forw_opt_band_id]["bidir"] = bidir + ''' + back_opt_band_id = 0 + if bidir: + self.opt_band_id += 1 + back_opt_band_id = self.opt_band_id + self.optical_bands[back_opt_band_id] = {} + self.optical_bands[back_opt_band_id]["optical_band_id"] = back_opt_band_id + self.optical_bands[back_opt_band_id]["bidir"] = bidir + self.optical_bands[back_opt_band_id]["reverse_optical_band_id"] = forw_opt_band_id + self.optical_bands[forw_opt_band_id]["reverse_optical_band_id"] = back_opt_band_id + else: + self.optical_bands[forw_opt_band_id]["reverse_optical_band_id"] = 0 + ''' + op = 0 + temp_links = [] + #num_slots = "all" + if self.nodes_dict[path[0]]["type"] == "OC-TP": + add_link = links[0] + temp_links.append(add_link) + links.remove(add_link) + path.remove(path[0]) + self.optical_bands[forw_opt_band_id]["src"] = path[0] + ''' + if bidir: + self.optical_bands[back_opt_band_id]["dst"] = path[0] + ''' + if self.nodes_dict[path[-1]]["type"] == "OC-TP": + drop_link = links[-1] + temp_links.append(drop_link) + links.remove(drop_link) + path.remove(path[-1]) + self.optical_bands[forw_opt_band_id]["dst"] = path[-1] + ''' + if bidir: + self.optical_bands[back_opt_band_id]["src"] = path[-1] + ''' + + c_slots, l_slots, s_slots = self.get_slots(links, num_slots) + if debug: + print(c_slots) + print(l_slots) + print(s_slots) + if len(c_slots) > 0 or len(l_slots) > 0 or len(s_slots) > 0: + flow_list, band_range, slots, fiber_f, fiber_b = self.select_slots_and_ports(links, num_slots, c_slots, l_slots, s_slots, bidir) + f0, band = freqency_converter(band_range, slots) + print(flow_list, band_range, slots, fiber_f, fiber_b) + ''' + + flow_list_b = {} + rev_path = path.copy() + rev_path.reverse() + rev_links = reverse_links(links) + if bidir: + for dev_x in flow_list.keys(): + flow_list_b[dev_x] = {} + flow_list_b[dev_x]["f"] = flow_list[dev_x]["b"] + del flow_list[dev_x]["b"] + rev_path = path.copy() + ''' + if debug: + print(f0, band) + print("INFO: RSA completed for optical band") + if flow_list is None: + self.null_values(self.flow_id) + return self.flow_id, [] + slots_i = [] + for i in slots: + slots_i.append(int(i)) + + # return links, path, flow_list, band_range, slots, fiber_f, fiber_b, op, num_slots, f0, band + # links, path, flows, bx, slots, fiber_f, fiber_b, op, n_slots, f0, band + if len(flow_list) > 0: + src_port = flow_list[path[0]]['f']['out'] + dst_port = flow_list[path[-1]]['f']['in'] + print(flow_list) + if len(fiber_f.keys()) == 1: + link_x = list(fiber_f.keys())[0] + #fib_x = fiber_f[link_x] + #rev_dst_port = self.links_dict[link_x]['fibers'][fib_x]["local_peer_port"] + #rev_src_port = self.links_dict[link_x]['fibers'][fib_x]["remote_peer_port"] + fibx = self.get_fiber_details(link_x, fiber_f[link_x]) + rev_dst_port = fibx["local_peer_port"] + rev_src_port = fibx["remote_peer_port"] + else: + link_in = list(fiber_f.keys())[0] + link_out = list(fiber_f.keys())[-1] + fib_inx = self.get_fiber_details(link_in, fiber_f[link_in]) + fib_outx = self.get_fiber_details(link_out, fiber_f[link_out]) + rev_dst_port = fib_inx["local_peer_port"] + rev_src_port = fib_outx["remote_peer_port"] + + #fib_in = fiber_f[link_in] + #fib_out = fiber_f[link_out] + #rev_dst_port = self.links_dict[link_in]['fibers'][fib_in]["local_peer_port"] + #rev_src_port = self.links_dict[link_out]['fibers'][fib_out]["remote_peer_port"] + + self.optical_bands[forw_opt_band_id]["flows"] = flow_list + self.optical_bands[forw_opt_band_id]["band_type"] = band_range + self.optical_bands[forw_opt_band_id]["fiber_forward"] = fiber_f + self.optical_bands[forw_opt_band_id]["fiber_backward"] = fiber_b + self.optical_bands[forw_opt_band_id]["op-mode"] = op + self.optical_bands[forw_opt_band_id]["n_slots"] = num_slots + self.optical_bands[forw_opt_band_id]["links"] = links + self.optical_bands[forw_opt_band_id]["path"] = path + self.optical_bands[forw_opt_band_id]["band"] = band + self.optical_bands[forw_opt_band_id]["freq"] = f0 + self.optical_bands[forw_opt_band_id]["is_active"] = True + self.optical_bands[forw_opt_band_id]["src_port"] = src_port + self.optical_bands[forw_opt_band_id]["dst_port"] = dst_port + self.optical_bands[forw_opt_band_id]["rev_dst_port"] = rev_dst_port + self.optical_bands[forw_opt_band_id]["rev_src_port"] = rev_src_port + self.optical_bands[forw_opt_band_id][band_range] = slots_i + self.optical_bands[forw_opt_band_id]["served_lightpaths"] = [] + ''' + if bidir: + self.optical_bands[back_opt_band_id]["flows"] = flow_list_b + self.optical_bands[back_opt_band_id]["band_type"] = band_range + self.optical_bands[back_opt_band_id]["fiber_forward"] = fiber_b + # self.optical_bands[back_opt_band_id]["fiber_backward"] = fiber_b + self.optical_bands[back_opt_band_id]["op-mode"] = op + self.optical_bands[back_opt_band_id]["n_slots"] = num_slots + self.optical_bands[back_opt_band_id]["links"] = rev_links + self.optical_bands[back_opt_band_id]["path"] = rev_path + self.optical_bands[back_opt_band_id]["band"] = band + self.optical_bands[back_opt_band_id]["freq"] = f0 + self.optical_bands[back_opt_band_id]["is_active"] = True + self.optical_bands[back_opt_band_id]["src_port"] = rev_src_port + self.optical_bands[back_opt_band_id]["dst_port"] = rev_dst_port + self.optical_bands[back_opt_band_id][band_range] = slots_i.copy() + self.optical_bands[back_opt_band_id]["served_lightpaths"] = [] + ''' + + return forw_opt_band_id, temp_links + + def get_optical_bands(self, r_src, r_dst): + result = [] + for ob_id in self.optical_bands: + ob = self.optical_bands[ob_id] + if debug: + print(r_src, ob["src"]) + print(r_dst, ob["dst"]) + print(ob) + if ob["src"] == r_src and ob["dst"] == r_dst: + result.append(ob_id) + return result + + def rsa_fs_computation(self, src, dst, rate, bidir, band): + num_slots_ob = "full_band" + if band is not None: + num_slots_ob = map_band_to_slot(band) + print(band, num_slots_ob) + if self.nodes_dict[src]["type"] == "OC-ROADM" and self.nodes_dict[dst]["type"] == "OC-ROADM": + print("INFO: ROADM to ROADM connection") + links, path = self.compute_path(src, dst) + if len(path) < 1: + self.null_values_ob(self.opt_band_id) + return self.flow_id, [] + optical_band_id, temp_links = self.create_optical_band(links, path, bidir, num_slots_ob) + return None, optical_band_id + self.flow_id += 1 + self.db_flows[self.flow_id] = {} + self.db_flows[self.flow_id]["flow_id"] = self.flow_id + self.db_flows[self.flow_id]["src"] = src + self.db_flows[self.flow_id]["dst"] = dst + self.db_flows[self.flow_id]["bitrate"] = rate + self.db_flows[self.flow_id]["bidir"] = bidir + print("INFO: TP to TP connection") + if band is None: + temp_links2 = [] + temp_path = [] + src_links = get_links_from_node(self.links_dict, src) + dst_links = get_links_to_node(self.links_dict, dst) + if len(src_links.keys()) >= 1: + temp_links2.append(list(src_links.keys())[0]) + if len(dst_links.keys()) >= 1: + temp_links2.append(list(dst_links.keys())[0]) + + if len(temp_links2) == 2: + [t_src, roadm_src] = temp_links2[0].split('-') + [roadm_dst, t_dst] = temp_links2[1].split('-') + temp_path.append(t_src) + temp_path.append(roadm_src) + temp_path.append(roadm_dst) + temp_path.append(t_dst) + existing_ob = self.get_optical_bands(roadm_src, roadm_dst) + + + if len(existing_ob) > 0: + print("INFO: Evaluating existing OB {}".format(existing_ob)) + #first checking in existing OB + ob_found = 0 + for ob_id in existing_ob: + op, num_slots = map_rate_to_slot(rate) + if debug: + print(temp_links2) + c_slots, l_slots, s_slots = self.get_slots(temp_links2, num_slots, ob_id) + if debug: + print(c_slots) + print(l_slots) + print(s_slots) + if len(c_slots) >= num_slots or len(l_slots) >= num_slots or len(s_slots) >= num_slots: + flow_list, band_range, slots, fiber_f, fiber_b = self.select_slots_and_ports_fs(temp_links2, num_slots, + c_slots, + l_slots, s_slots, bidir, + ob_id) + f0, band = freqency_converter(band_range, slots) + if debug: + print(f0, band) + print("INFO: RSA completed for Flex Lightpath with OB already in place") + if flow_list is None: + self.null_values(self.flow_id) + continue + slots_i = [] + for i in slots: + slots_i.append(int(i)) + # return links, path, flow_list, band_range, slots, fiber_f, fiber_b, op, num_slots, f0, band + # links, path, flows, bx, slots, fiber_f, fiber_b, op, n_slots, f0, band + self.db_flows[self.flow_id]["flows"] = flow_list + self.db_flows[self.flow_id]["band_type"] = band_range + self.db_flows[self.flow_id]["slots"] = slots_i + self.db_flows[self.flow_id]["fiber_forward"] = fiber_f + self.db_flows[self.flow_id]["fiber_backward"] = fiber_b + self.db_flows[self.flow_id]["op-mode"] = op + self.db_flows[self.flow_id]["n_slots"] = num_slots + self.db_flows[self.flow_id]["links"] = temp_links2 + self.db_flows[self.flow_id]["path"] = temp_path + self.db_flows[self.flow_id]["band"] = band + self.db_flows[self.flow_id]["freq"] = f0 + self.db_flows[self.flow_id]["is_active"] = True + self.db_flows[self.flow_id]["parent_opt_band"] = ob_id + self.db_flows[self.flow_id]["new_optical_band"] = 0 + self.optical_bands[ob_id]["served_lightpaths"].append(self.flow_id) + ''' + if bidir: + rev_ob_id = self.optical_bands[ob_id]["reverse_optical_band_id"] + self.optical_bands[rev_ob_id]["served_lightpaths"].append(self.flow_id) + ''' + return self.flow_id, ob_id + else: + print("not enough slots") + if band is None: + print("INFO: Not existing optical-band meeting the requirements") + else: + print("INFO: optical-band width specified") + #if no OB I create a new one + links, path = self.compute_path(src, dst) + optical_band_id, temp_links = self.create_optical_band(links, path, bidir, num_slots_ob) + op, num_slots = map_rate_to_slot(rate) + # self.flow_id += 1 + # self.db_flows[self.flow_id] = {} + # self.db_flows[self.flow_id]["flow_id"] = self.flow_id + # self.db_flows[self.flow_id]["src"] = src + # self.db_flows[self.flow_id]["dst"] = dst + # self.db_flows[self.flow_id]["bitrate"] = rate + # self.db_flows[self.flow_id]["bidir"] = bidir + + if debug: + print(temp_links) + c_slots, l_slots, s_slots = self.get_slots(temp_links, num_slots, optical_band_id) + if debug: + print(c_slots) + print(l_slots) + print(s_slots) + if len(c_slots) > 0 or len(l_slots) > 0 or len(s_slots) > 0: + flow_list, band_range, slots, fiber_f, fiber_b = self.select_slots_and_ports_fs(temp_links, num_slots, c_slots, + l_slots, s_slots, bidir, optical_band_id) + f0, band = freqency_converter(band_range, slots) + if debug: + print(f0, band) + print("INFO: RSA completed for FLex Lightpath with new OB") + if flow_list is None: + self.null_values(self.flow_id) + return self.flow_id, optical_band_id + slots_i = [] + for i in slots: + slots_i.append(int(i)) + + self.db_flows[self.flow_id]["flows"] = flow_list + self.db_flows[self.flow_id]["band_type"] = band_range + self.db_flows[self.flow_id]["slots"] = slots_i + self.db_flows[self.flow_id]["fiber_forward"] = fiber_f + self.db_flows[self.flow_id]["fiber_backward"] = fiber_b + self.db_flows[self.flow_id]["op-mode"] = op + self.db_flows[self.flow_id]["n_slots"] = num_slots + self.db_flows[self.flow_id]["links"] = temp_links + self.db_flows[self.flow_id]["path"] = path + self.db_flows[self.flow_id]["band"] = band + self.db_flows[self.flow_id]["freq"] = f0 + self.db_flows[self.flow_id]["is_active"] = True + self.db_flows[self.flow_id]["parent_opt_band"] = optical_band_id + self.db_flows[self.flow_id]["new_optical_band"] = 1 + self.optical_bands[optical_band_id]["served_lightpaths"].append(self.flow_id) + ''' + if bidir: + rev_ob_id = self.optical_bands[optical_band_id]["reverse_optical_band_id"] + self.optical_bands[rev_ob_id]["served_lightpaths"].append(self.flow_id) + ''' + return self.flow_id, optical_band_id diff --git a/src/opticalcontroller/__init__.py b/src/opticalcontroller/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..38d04994fb0fa1951fb465bc127eb72659dc2eaf --- /dev/null +++ b/src/opticalcontroller/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/src/opticalcontroller/dijsktra.py b/src/opticalcontroller/dijsktra.py new file mode 100644 index 0000000000000000000000000000000000000000..a86d1d93dcbc30b9e4e3b95a34a0922439871bf6 --- /dev/null +++ b/src/opticalcontroller/dijsktra.py @@ -0,0 +1,240 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# TODO: migrate to NetworkX: +# https://networkx.org/documentation/stable/index.html +# https://networkx.org/documentation/stable/reference/algorithms/shortest_paths.html + +import sys + +class Vertex: + def __init__(self, node): + self.id = node + self.adjacent = {} + # Set distance to infinity for all nodes + self.distance = float("inf") + # Mark all nodes unvisited + self.visited = False + # Predecessor + self.previous = None + + # heapq compara gli item nella coda usando <= per vedere ci sono duplciati: + # se ho una coda di tuple, + # compara il primo elemento della prima tupla nella coda con il primo elemento della seconda tupla nella coda + # se sono diversi si ferma, se sono uguali continua + # la tupla nel caso in esame è: (v.get_distance(),v) + # se due nodi hanno stessa distanza, heapq procede a comparare v: Vertex(). + # Va quindi definita una politica per confrontare i Vertex + def __lt__(self, other): + if self.id < other.id: + return True + else: + return False + + def __le__(self, other): + if self.id <= other.id: + return True + else: + return False + + def add_neighbor(self, neighbor, port): + self.adjacent[neighbor] = port + + def del_neighbor(self, neighbor): + self.adjacent.pop(neighbor) + + def get_connections(self): + return self.adjacent.keys() + + def get_id(self): + return self.id + + def get_port(self, neighbor): + return self.adjacent[neighbor][0] + + def get_weight(self, neighbor): + return self.adjacent[neighbor][1] + + def set_distance(self, dist): + self.distance = dist + + def get_distance(self): + return self.distance + + def set_previous(self, prev): + self.previous = prev + + def set_visited(self): + self.visited = True + + def reset_vertex(self): + self.visited = False + self.previous = None + self.distance = float("inf") + + def __str__(self): + return str(self.id) + ' adjacent: ' + str([x.id for x in self.adjacent]) + +class Graph: + def __init__(self): + self.vert_dict = {} + self.num_vertices = 0 + + def __iter__(self): + return iter(self.vert_dict.values()) + + def reset_graph(self): + for n in self.vert_dict: + self.get_vertex(n).reset_vertex() + + def printGraph(self): + for v in self: + for w in v.get_connections(): + vid = v.get_id() + wid = w.get_id() + print ('( %s , %s, %s, %s, %s, %s)' % ( vid, wid, v.get_port(w), w.get_port(v), v.get_weight(w), w.get_weight(v))) + + def add_vertex(self, node): + self.num_vertices = self.num_vertices + 1 + new_vertex = Vertex(node) + self.vert_dict[node] = new_vertex + return new_vertex + + def del_Vertex(self, node): + self.vert_dict.pop(node) + + def get_vertex(self, n): + if n in self.vert_dict: + return self.vert_dict[n] + else: + return None + + def add_edge(self, frm, to, port_frm, port_to,w): + if frm not in self.vert_dict: + self.add_vertex(frm) + if to not in self.vert_dict: + self.add_vertex(to) + + self.vert_dict[frm].add_neighbor(self.vert_dict[to], [port_frm, w]) + self.vert_dict[to].add_neighbor(self.vert_dict[frm], [port_to, w]) + + def del_edge(self, frm, to, cost = 0): + self.vert_dict[frm].del_neighbor(self.vert_dict[to]) + self.vert_dict[to].del_neighbor(self.vert_dict[frm]) + + def get_vertices(self): + return self.vert_dict.keys() + + def set_previous(self, current): + self.previous = current + + def get_previous(self, current): + return self.previous + +def shortest(v, path): + if v.previous: + path.append(v.previous.get_id()) + shortest(v.previous, path) + return + +import heapq + +def dijkstra(aGraph, start): + """print ('''Dijkstra's shortest path''')""" + # Set the distance for the start node to zero + start.set_distance(0) + + # Put tuple pair into the priority queue + unvisited_queue = [(v.get_distance(),v) for v in aGraph] + #priority queue->costruisce un albero in cui ogni nodo parent ha ha un valore <= di ogni child + #heappop prende il valore più piccolo, nel caso di dikstra, il nodo più vicino + heapq.heapify(unvisited_queue) + + while len(unvisited_queue): + # Pops a vertex with the smallest distance + uv = heapq.heappop(unvisited_queue) + current = uv[1] + current.set_visited() + + #for next in v.adjacent: + for next in current.adjacent: + # if visited, skip + if next.visited: + continue + new_dist = current.get_distance() + current.get_weight(next) + + if new_dist < next.get_distance(): + next.set_distance(new_dist) + next.set_previous(current) + """print ('updated : current = %s next = %s new_dist = %s' \ + %(current.get_id(), next.get_id(), next.get_distance()))""" + else: + """print ('not updated : current = %s next = %s new_dist = %s' \ + %(current.get_id(), next.get_id(), next.get_distance()))""" + + # Rebuild heap + # 1. Pop every item + while len(unvisited_queue): + heapq.heappop(unvisited_queue) + # 2. Put all vertices not visited into the queue + unvisited_queue = [(v.get_distance(),v) for v in aGraph if not v.visited] + heapq.heapify(unvisited_queue) + +def shortest_path(graph, src, dst): + dijkstra(graph, src) + target = dst + path = [target.get_id()] + shortest(target, path) + return path[::-1] + +if __name__ == '__main__': + + print("Testing Algo") + g = Graph() + + g.add_vertex('a') + g.add_vertex('b') + g.add_vertex('c') + g.add_vertex('d') + g.add_vertex('e') + g.add_vertex('f') + + g.add_edge('a', 'b', 7) + g.add_edge('a', 'c', 9) + g.add_edge('a', 'f', 14) + g.add_edge('b', 'c', 10) + g.add_edge('b', 'd', 15) + g.add_edge('c', 'd', 11) + g.add_edge('c', 'f', 2) + g.add_edge('d', 'e', 6) + g.add_edge('e', 'f', 9) + + + """print ('Graph data:') + for v in g: + for w in v.get_connections(): + vid = v.get_id() + wid = w.get_id() + print ('( %s , %s, %3d)' % ( vid, wid, v.get_weight(w))) + + + dijkstra(g, g.get_vertex('a')) + + target = g.get_vertex('e') + path = [target.get_id()] + shortest(target, path) + print ('The shortest path : %s' %(path[::-1]))""" + + p = shortest_path(g, g.get_vertex('a'), g.get_vertex('e')) + print(p) diff --git a/src/opticalcontroller/images/topo.png b/src/opticalcontroller/images/topo.png new file mode 100644 index 0000000000000000000000000000000000000000..216e1360b3caecd68285ca8b69fd498049dcbf79 Binary files /dev/null and b/src/opticalcontroller/images/topo.png differ diff --git a/src/opticalcontroller/json_files/nodes.json b/src/opticalcontroller/json_files/nodes.json new file mode 100644 index 0000000000000000000000000000000000000000..60f017c19d7c7a578c0ddfc2225cab742deb0026 --- /dev/null +++ b/src/opticalcontroller/json_files/nodes.json @@ -0,0 +1,39 @@ +{ + "R1":{ + "id":0, + "ip":"10.30.2.207", + "port":"50001", + "type":"OC-ROADM", + "driver": "OpticalOC" + }, + + "R2":{ + "id":1, + "ip":"10.30.2.208", + "port":"50001", + "type":"OC-ROADM", + "driver": "OpticalOC" + }, + + "R3":{ + "id":2, + "ip":"10.30.2.209", + "port":"50001", + "type":"OC-ROADM", + "driver": "OpticalOC" + }, + "T1":{ + "id":3, + "ip":"10.30.2.210", + "port":"50001", + "type":"OC-TP", + "driver": "OpticalOC" + }, + "T2":{ + "id":4, + "ip":"10.30.2.211", + "port":"50001", + "type":"OC-TP", + "driver": "OpticalOC" + } +} diff --git a/src/opticalcontroller/json_files/optical_TFSworking.json b/src/opticalcontroller/json_files/optical_TFSworking.json new file mode 100644 index 0000000000000000000000000000000000000000..ff1841eeea9df1e73bcfb25d07f19d64554f11e1 --- /dev/null +++ b/src/opticalcontroller/json_files/optical_TFSworking.json @@ -0,0 +1,486 @@ +{ + "R1-R2": { + "length": 80, + "source": "d1", + "target": "d1", + "fibers": { + "d1-1": { + "length": 80, + "src_port": "3", + "dst_port": "14", + "local_peer_port": "13", + "remote_peer_port": "4", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R2-R1": { + "length": 80, + "source": "d1", + "target": "d1", + "fibers": { + "d1-1": { + "length": 80, + "src_port": "4", + "dst_port": "13", + "local_peer_port": "14", + "remote_peer_port": "3", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "T1-R1": { + "length": 0, + "source": "muxT", + "target": "srgR", + "fibers": { + "M1": { + "length": 0, + "src_port": "1", + "dst_port": "12", + "local_peer_port": "1", + "remote_peer_port": "2", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R1-T1": { + "length": 0, + "source": "srgT", + "target": "muxR", + "fibers": { + "S1": { + "length": 0, + "src_port": "2", + "dst_port": "1", + "local_peer_port": "12", + "remote_peer_port": "1", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "T2-R2": { + "length": 0, + "source": "muxT", + "target": "srgR", + "fibers": { + "M1": { + "length": 0, + "src_port": "6", + "dst_port": "15", + "local_peer_port": "6", + "remote_peer_port": "5", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R2-T2": { + "length": 0, + "source": "srgT", + "target": "muxR", + "fibers": { + "S1": { + "length": 0, + "src_port": "5", + "dst_port": "6", + "local_peer_port": "15", + "remote_peer_port": "6", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + } +} diff --git a/src/opticalcontroller/json_files/optical_topoTFS.json b/src/opticalcontroller/json_files/optical_topoTFS.json new file mode 100644 index 0000000000000000000000000000000000000000..7dea474cd676b7c699cffc1c180e14598b987473 --- /dev/null +++ b/src/opticalcontroller/json_files/optical_topoTFS.json @@ -0,0 +1,1836 @@ +{ + "R1-R2": { + "length": 80, + "source": "d1", + "target": "d1", + "fibers": { + "d1-1": { + "length": 80, + "src_port": "101", + "dst_port": "201", + "local_peer_port": "201", + "remote_peer_port": "101", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d1-2": { + "length": 80, + "src_port": "102", + "dst_port": "202", + "local_peer_port": "202", + "remote_peer_port": "102", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R2-R1": { + "length": 80, + "source": "d1", + "target": "d1", + "fibers": { + "d1-1": { + "length": 80, + "src_port": "101", + "dst_port": "201", + "local_peer_port": "201", + "remote_peer_port": "101", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d1-2": { + "length": 80, + "src_port": "102", + "dst_port": "202", + "local_peer_port": "202", + "remote_peer_port": "102", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R1-R3": { + "length": 80, + "source": "d2", + "target": "d1", + "fibers": { + "d2-1": { + "length": 80, + "src_port": "103", + "dst_port": "201", + "local_peer_port": "203", + "remote_peer_port": "101", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d2-2": { + "length": 80, + "src_port": "104", + "dst_port": "202", + "local_peer_port": "204", + "remote_peer_port": "102", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R3-R1": { + "length": 80, + "source": "d1", + "target": "d2", + "fibers": { + "d1-1": { + "length": 80, + "src_port": "101", + "dst_port": "203", + "local_peer_port": "201", + "remote_peer_port": "103", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d1-2": { + "length": 80, + "src_port": "102", + "dst_port": "204", + "local_peer_port": "202", + "remote_peer_port": "104", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R2-R3": { + "length": 80, + "source": "d2", + "target": "d2", + "fibers": { + "d2-1": { + "length": 80, + "src_port": "103", + "dst_port": "203", + "local_peer_port": "203", + "remote_peer_port": "103", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d2-2": { + "length": 80, + "src_port": "104", + "dst_port": "204", + "local_peer_port": "204", + "remote_peer_port": "104", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R3-R2": { + "length": 80, + "source": "d2", + "target": "d2", + "fibers": { + "d2-1": { + "length": 80, + "src_port": "103", + "dst_port": "203", + "local_peer_port": "203", + "remote_peer_port": "103", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d2-2": { + "length": 80, + "src_port": "104", + "dst_port": "204", + "local_peer_port": "204", + "remote_peer_port": "104", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "T1-R1": { + "length": 0, + "source": "muxT", + "target": "srgR", + "fibers": { + "M1": { + "length": 0, + "src_port": "1", + "dst_port": "2001", + "local_peer_port": "1", + "remote_peer_port": "1001", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "M2": { + "length": 0, + "src_port": "2", + "dst_port": "2002", + "local_peer_port": "2", + "remote_peer_port": "1002", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "M3": { + "length": 0, + "src_port": "3", + "dst_port": "2003", + "local_peer_port": "3", + "remote_peer_port": "1003", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R1-T1": { + "length": 0, + "source": "srgT", + "target": "muxR", + "fibers": { + "S1": { + "length": 0, + "src_port": "1001", + "dst_port": "1", + "local_peer_port": "2001", + "remote_peer_port": "1", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "S2": { + "length": 0, + "src_port": "1002", + "dst_port": "2", + "local_peer_port": "2002", + "remote_peer_port": "2", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "S3": { + "length": 0, + "src_port": "1003", + "dst_port": "3", + "local_peer_port": "2003", + "remote_peer_port": "3", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "T2-R2": { + "length": 0, + "source": "muxT", + "target": "srgR", + "fibers": { + "M1": { + "length": 0, + "src_port": "1", + "dst_port": "2001", + "local_peer_port": "1", + "remote_peer_port": "1001", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "M2": { + "length": 0, + "src_port": "2", + "dst_port": "2002", + "local_peer_port": "2", + "remote_peer_port": "1002", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "M3": { + "length": 0, + "src_port": "3", + "dst_port": "2003", + "local_peer_port": "3", + "remote_peer_port": "1003", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R2-T2": { + "length": 0, + "source": "srgT", + "target": "muxR", + "fibers": { + "S1": { + "length": 0, + "src_port": "1001", + "dst_port": "1", + "local_peer_port": "2001", + "remote_peer_port": "1", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "S2": { + "length": 0, + "src_port": "1002", + "dst_port": "2", + "local_peer_port": "2002", + "remote_peer_port": "2", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "S3": { + "length": 0, + "src_port": "1003", + "dst_port": "3", + "local_peer_port": "2003", + "remote_peer_port": "3", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + } +} diff --git a/src/opticalcontroller/json_files/tfs.json b/src/opticalcontroller/json_files/tfs.json new file mode 100644 index 0000000000000000000000000000000000000000..31803b893b5639e957be33465599573baa475ca2 --- /dev/null +++ b/src/opticalcontroller/json_files/tfs.json @@ -0,0 +1,686 @@ +{ + "links": [ + { + "link_id": { + "link_uuid": { + "uuid": "T1->R1" + } + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "T1" + } + }, + "endpoint_uuid": { + "uuid": "1" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "R1" + } + }, + "endpoint_uuid": { + "uuid": "12" + } + } + ], + "optical_link": { + "name": "T1-R1", + "details": { + "length": 0, + "source": "muxT", + "target": "srgR", + "fibers": [ + { + "ID": "M1", + "length": 0, + "src_port": "1", + "dst_port": "12", + "local_peer_port": "1", + "remote_peer_port": "2", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + ] + } + } + }, + { + "link_id": { + "link_uuid": { + "uuid": "R1->T1" + } + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "R1" + } + }, + "endpoint_uuid": { + "uuid": "2" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "T1" + } + }, + "endpoint_uuid": { + "uuid": "1" + } + } + ], + "optical_link": { + "name": "R1-T1", + "details": { + "length": 0, + "source": "srgT", + "target": "muxT", + "fibers": [ + { + "ID": "M1", + "length": 0, + "src_port": "2", + "dst_port": "1", + "local_peer_port": "12", + "remote_peer_port": "1", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + ] + } + } + }, + { + "link_id": { + "link_uuid": { + "uuid": "R1->R2" + } + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "R1" + } + }, + "endpoint_uuid": { + "uuid": "3" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "R2" + } + }, + "endpoint_uuid": { + "uuid": "14" + } + } + ], + "optical_link": { + "name": "R1-R2", + "details": { + "length": 0, + "source": "D1", + "target": "D1", + "fibers": [ + { + "ID": "D11", + "length": 0, + "src_port": "3", + "dst_port": "14", + "local_peer_port": "13", + "remote_peer_port": "4", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + ] + } + } + }, + { + "link_id": { + "link_uuid": { + "uuid": "R2->R1" + } + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "R2" + } + }, + "endpoint_uuid": { + "uuid": "4" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "R1" + } + }, + "endpoint_uuid": { + "uuid": "13" + } + } + ], + "optical_link": { + "name": "R2-R1", + "details": { + "length": 0, + "source": "D1", + "target": "D1", + "fibers": [ + { + "ID": "D11", + "length": 0, + "src_port": "4", + "dst_port": "13", + "local_peer_port": "14", + "remote_peer_port": "3", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + ] + } + } + }, + { + "link_id": { + "link_uuid": { + "uuid": "T2->R2" + } + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "T2" + } + }, + "endpoint_uuid": { + "uuid": "6" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "R2" + } + }, + "endpoint_uuid": { + "uuid": "15" + } + } + ], + "optical_link": { + "name": "T2-R2", + "details": { + "length": 0, + "source": "srgT", + "target": "muxT", + "fibers": [ + { + "ID": "M1", + "length": 0, + "src_port": "6", + "dst_port": "15", + "local_peer_port": "6", + "remote_peer_port": "5", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + ] + } + } + }, + { + "link_id": { + "link_uuid": { + "uuid": "R2->T2" + } + }, + "link_endpoint_ids": [ + { + "device_id": { + "device_uuid": { + "uuid": "R2" + } + }, + "endpoint_uuid": { + "uuid": "5" + } + }, + { + "device_id": { + "device_uuid": { + "uuid": "T2" + } + }, + "endpoint_uuid": { + "uuid": "6" + } + } + ], + "optical_link": { + "name": "R2-T2", + "details": { + "length": 0, + "source": "srgT", + "target": "muxT", + "fibers": [ + { + "ID": "M1", + "length": 0, + "src_port": "5", + "dst_port": "6", + "local_peer_port": "15", + "remote_peer_port": "6", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/src/opticalcontroller/json_files/topo_2_links.json b/src/opticalcontroller/json_files/topo_2_links.json new file mode 100644 index 0000000000000000000000000000000000000000..02165938ce675071a4ff4c3424e3b53244d40810 --- /dev/null +++ b/src/opticalcontroller/json_files/topo_2_links.json @@ -0,0 +1,1530 @@ +{ + "R1-R3": { + "length": 80, + "source": "d2", + "target": "d1", + "fibers": { + "d2-1": { + "length": 80, + "src_port": "103", + "dst_port": "201", + "local_peer_port": "203", + "remote_peer_port": "101", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d2-2": { + "length": 80, + "src_port": "104", + "dst_port": "202", + "local_peer_port": "204", + "remote_peer_port": "102", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R3-R1": { + "length": 80, + "source": "d1", + "target": "d2", + "fibers": { + "d1-1": { + "length": 80, + "src_port": "101", + "dst_port": "203", + "local_peer_port": "201", + "remote_peer_port": "103", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d1-2": { + "length": 80, + "src_port": "102", + "dst_port": "204", + "local_peer_port": "202", + "remote_peer_port": "104", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R2-R3": { + "length": 80, + "source": "d2", + "target": "d2", + "fibers": { + "d2-1": { + "length": 80, + "src_port": "103", + "dst_port": "203", + "local_peer_port": "203", + "remote_peer_port": "103", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d2-2": { + "length": 80, + "src_port": "104", + "dst_port": "204", + "local_peer_port": "204", + "remote_peer_port": "104", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R3-R2": { + "length": 80, + "source": "d2", + "target": "d2", + "fibers": { + "d2-1": { + "length": 80, + "src_port": "103", + "dst_port": "203", + "local_peer_port": "203", + "remote_peer_port": "103", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "d2-2": { + "length": 80, + "src_port": "104", + "dst_port": "204", + "local_peer_port": "204", + "remote_peer_port": "104", + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "T1-R1": { + "length": 0, + "source": "muxT", + "target": "srgR", + "fibers": { + "M1": { + "length": 0, + "src_port": "1", + "dst_port": "2001", + "local_peer_port": "1", + "remote_peer_port": "1001", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "M2": { + "length": 0, + "src_port": "2", + "dst_port": "2002", + "local_peer_port": "2", + "remote_peer_port": "1002", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "M3": { + "length": 0, + "src_port": "3", + "dst_port": "2003", + "local_peer_port": "3", + "remote_peer_port": "1003", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R1-T1": { + "length": 0, + "source": "srgT", + "target": "muxR", + "fibers": { + "S1": { + "length": 0, + "src_port": "1001", + "dst_port": "1", + "local_peer_port": "2001", + "remote_peer_port": "1", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "S2": { + "length": 0, + "src_port": "1002", + "dst_port": "2", + "local_peer_port": "2002", + "remote_peer_port": "2", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "S3": { + "length": 0, + "src_port": "1003", + "dst_port": "3", + "local_peer_port": "2003", + "remote_peer_port": "3", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "T2-R2": { + "length": 0, + "source": "muxT", + "target": "srgR", + "fibers": { + "M1": { + "length": 0, + "src_port": "1", + "dst_port": "2001", + "local_peer_port": "1", + "remote_peer_port": "1001", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "M2": { + "length": 0, + "src_port": "2", + "dst_port": "2002", + "local_peer_port": "2", + "remote_peer_port": "1002", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "M3": { + "length": 0, + "src_port": "3", + "dst_port": "2003", + "local_peer_port": "3", + "remote_peer_port": "1003", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + }, + "R2-T2": { + "length": 0, + "source": "srgT", + "target": "muxR", + "fibers": { + "S1": { + "length": 0, + "src_port": "1001", + "dst_port": "1", + "local_peer_port": "2001", + "remote_peer_port": "1", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "S2": { + "length": 0, + "src_port": "1002", + "dst_port": "2", + "local_peer_port": "2002", + "remote_peer_port": "2", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + }, + "S3": { + "length": 0, + "src_port": "1003", + "dst_port": "3", + "local_peer_port": "2003", + "remote_peer_port": "3", + "used": false, + "c_slots": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20 + ], + "l_slots": [ + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120 + ], + "s_slots": [ + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520 + ] + } + } + } +} diff --git a/src/opticalcontroller/json_files/topology-optical.json b/src/opticalcontroller/json_files/topology-optical.json new file mode 100644 index 0000000000000000000000000000000000000000..e2453b654d53fc3200570ce9c2b17effc964bd25 --- /dev/null +++ b/src/opticalcontroller/json_files/topology-optical.json @@ -0,0 +1,252 @@ +{ + "r1-r2": { + "length": 80, + "source" : "d1", + "target" : "d1", + "fibers" : { + "d1-1": { + "src_port": "1T", + "dst_port": "1R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "d1-2":{ + "src_port": "2T", + "dst_port": "2R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + }, + "r2-r1": { + "length": 80, + "source" : "d1", + "target" : "d1", + "fibers" : { + "d1-1" : { + "src_port": "1T", + "dst_port": "1R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "d1-2" : { + "src_port": "2T", + "dst_port": "2R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + }, + "r1-r3": { + "length": 80, + "source" : "d2", + "target" : "d1", + "fibers" : { + "d2-1":{ + "src_port": "3T", + "dst_port": "1R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "d2-2":{ + "src_port": "4T", + "dst_port": "2R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + }, + "r3-r1": { + "length": 80, + "source" : "d1", + "target" : "d2", + "fibers" : { + "d1-1": { + "src_port": "1T", + "dst_port": "3R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "d1-2": { + "src_port": "2T", + "dst_port": "4R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + }, + "r2-r3": { + "length": 80, + "source" : "d2", + "target" : "d2", + "fibers" : { + "d2-1": { + "src_port": "3T", + "dst_port": "3R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "d2-2": { + "src_port": "4T", + "dst_port": "4R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + }, + "r3-r2": { + "length": 80, + "source" : "d2", + "target" : "d2", + "fibers" : { + "d2-1": { + "src_port": "3T", + "dst_port": "3R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "d2-2":{ + "src_port": "4T", + "dst_port": "4R", + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + }, + "t1-r1": { + "length": 0, + "source" : "muxT", + "target" : "srgR", + "fibers" : { + "M1": { + "src_port": "1", + "dst_port": "101R", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "M2": { + "src_port": "2", + "dst_port": "102R", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "M3": { + "src_port": "3", + "dst_port": "103R", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + }, + "r1-t1": { + "length": 0, + "source" : "srgT", + "target" : "muxR", + "fibers" : { + "S1": { + "src_port": "101T", + "dst_port": "1", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "S2": { + "src_port": "102T", + "dst_port": "2", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "S3": { + "src_port": "103T", + "dst_port": "3", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + }, + "t2-r2": { + "length": 0, + "source" : "muxT", + "target" : "srgR", + "fibers" : { + "M1": { + "src_port": "1", + "dst_port": "101R", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "M2": { + "src_port": "2", + "dst_port": "102R", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "M3": { + "src_port": "3", + "dst_port": "103R", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + }, + "r2-t2": { + "length": 0, + "source" : "srgT", + "target" : "muxR", + "fibers" : { + "S1": { + "src_port": "101T", + "dst_port": "1", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "S2": { + "src_port": "102T", + "dst_port": "2", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + }, + "S3": { + "src_port": "103T", + "dst_port": "3", + "used": false, + "c_slots": {"1": 1, "2": 1, "3": 1, "4": 1}, + "l_slots": {"101": 1, "102": 1, "103": 1, "104": 1}, + "s_slots": {"1001": 1, "1002": 1, "1003": 1, "1004": 1} + } + } + } +} diff --git a/src/opticalcontroller/json_files/topology-optical2.json b/src/opticalcontroller/json_files/topology-optical2.json new file mode 100644 index 0000000000000000000000000000000000000000..fe8e9866bcc64d52b2f49089ce03af47d72df9d0 --- /dev/null +++ b/src/opticalcontroller/json_files/topology-optical2.json @@ -0,0 +1,324 @@ +{ + "r1-r2": { + "length": 80, + "source" : "d1", + "target" : "d1", + "fibers" : { + "d1-1": { + "length": 80, + "src_port": "1T", + "dst_port": "1R", + "local_peer_port": "1R", + "remote_peer_port": "1T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "d1-2":{ + "length": 80, + "src_port": "2T", + "dst_port": "2R", + "local_peer_port": "2R", + "remote_peer_port": "2T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + }, + "r2-r1": { + "length": 80, + "source" : "d1", + "target" : "d1", + "fibers" : { + "d1-1" : { + "length": 80, + "src_port": "1T", + "dst_port": "1R", + "local_peer_port": "1R", + "remote_peer_port": "1T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "d1-2" : { + "length": 80, + "src_port": "2T", + "dst_port": "2R", + "local_peer_port": "2R", + "remote_peer_port": "2T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + }, + "r1-r3": { + "length": 80, + "source" : "d2", + "target" : "d1", + "fibers" : { + "d2-1":{ + "length": 80, + "src_port": "3T", + "dst_port": "1R", + "local_peer_port": "3R", + "remote_peer_port": "1T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "d2-2":{ + "length": 80, + "src_port": "4T", + "dst_port": "2R", + "local_peer_port": "4R", + "remote_peer_port": "2T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + }, + "r3-r1": { + "length": 80, + "source" : "d1", + "target" : "d2", + "fibers" : { + "d1-1": { + "length": 80, + "src_port": "1T", + "dst_port": "3R", + "local_peer_port": "1R", + "remote_peer_port": "3T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "d1-2": { + "length": 80, + "src_port": "2T", + "dst_port": "4R", + "local_peer_port": "2R", + "remote_peer_port": "4T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + }, + "r2-r3": { + "length": 80, + "source" : "d2", + "target" : "d2", + "fibers" : { + "d2-1": { + "length": 80, + "src_port": "3T", + "dst_port": "3R", + "local_peer_port": "3R", + "remote_peer_port": "3T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "d2-2": { + "length": 80, + "src_port": "4T", + "dst_port": "4R", + "local_peer_port": "4R", + "remote_peer_port": "4T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + }, + "r3-r2": { + "length": 80, + "source" : "d2", + "target" : "d2", + "fibers" : { + "d2-1": { + "length": 80, + "src_port": "3T", + "dst_port": "3R", + "local_peer_port": "3R", + "remote_peer_port": "3T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "d2-2":{ + "length": 80, + "src_port": "4T", + "dst_port": "4R", + "local_peer_port": "4R", + "remote_peer_port": "4T", + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + }, + "t1-r1": { + "length": 0, + "source" : "muxT", + "target" : "srgR", + "fibers" : { + "M1": { + "length": 0, + "src_port": "1", + "dst_port": "101R", + "local_peer_port": "1", + "remote_peer_port": "101T", + "used": false, + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "M2": { + "length": 0, + "src_port": "2", + "dst_port": "102R", + "local_peer_port": "2", + "remote_peer_port": "102T", + "used": false, + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "M3": { + "length": 0, + "src_port": "3", + "dst_port": "103R", + "local_peer_port": "3", + "remote_peer_port": "103T", + "used": false, + "c_slots": [], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + }, + "r1-t1": { + "length": 0, + "source" : "srgT", + "target" : "muxR", + "fibers" : { + "S1": { + "length": 0, + "src_port": "101T", + "dst_port": "1", + "local_peer_port": "101R", + "remote_peer_port": "1", + "used": false, + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "S2": { + "length": 0, + "src_port": "102T", + "dst_port": "2", + "local_peer_port": "102T", + "remote_peer_port": "2", + "used": false, + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "S3": { + "length": 0, + "src_port": "103T", + "dst_port": "3", + "local_peer_port": "103R", + "remote_peer_port": "3", + "used": false, + "c_slots": [], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + }, + "t2-r2": { + "length": 0, + "source" : "muxT", + "target" : "srgR", + "fibers" : { + "M1": { + "length": 0, + "src_port": "1", + "dst_port": "101R", + "local_peer_port": "1", + "remote_peer_port": "101T", + "used": false, + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "M2": { + "length": 0, + "src_port": "2", + "dst_port": "102R", + "local_peer_port": "2", + "remote_peer_port": "102T", + "used": false, + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "M3": { + "length": 0, + "src_port": "3", + "dst_port": "103R", + "local_peer_port": "3", + "remote_peer_port": "103T", + "used": false, + "c_slots": [], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + }, + "r2-t2": { + "length": 0, + "source" : "srgT", + "target" : "muxR", + "fibers" : { + "S1": { + "length": 0, + "src_port": "101T", + "dst_port": "1", + "local_peer_port": "101R", + "remote_peer_port": "1", + "used": false, + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "S2": { + "length": 0, + "src_port": "102T", + "dst_port": "2", + "local_peer_port": "102R", + "remote_peer_port": "2", + "used": false, + "c_slots": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + }, + "S3": { + "length": 0, + "src_port": "103T", + "dst_port": "3", + "local_peer_port": "103R", + "remote_peer_port": "3", + "used": false, + "c_slots": [], + "l_slots": [101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 ,120], + "s_slots": [501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519 ,520] + } + } + } +} diff --git a/src/opticalcontroller/requirements.in b/src/opticalcontroller/requirements.in new file mode 100644 index 0000000000000000000000000000000000000000..fefe604bc6f0d8d84c56817e7e85bb41015240ad --- /dev/null +++ b/src/opticalcontroller/requirements.in @@ -0,0 +1,21 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Flask==1.1.2 +flask-restplus==0.13.0 +itsdangerous==1.1.0 +Jinja2==2.11.3 +MarkupSafe==1.1.1 +numpy==1.23.0 +Werkzeug==0.16.1 diff --git a/src/opticalcontroller/tools.py b/src/opticalcontroller/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..3b3223d81b2fe4e80956c02afae1a71c429493cf --- /dev/null +++ b/src/opticalcontroller/tools.py @@ -0,0 +1,189 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from variables import * +import json + + +def common_slots(a, b): + return list(np.intersect1d(a, b)) + + +def map_modulation_to_op(mod): + if mod == "DP-QPSK": + return 1 + if mod == "DP-16QAM": + return 7 + if mod == "DP-64QAM": + return 10 + + +def map_band_to_slot(band): + return int(band/12.5) + + +def map_rate_to_slot(rate): + if rate == 100: + mod = "DP-QPSK" + slots = 4 + op = map_modulation_to_op(mod) + return op, slots + if rate == 400: + mod = "DP-16QAM" + slots = 8 + op = map_modulation_to_op(mod) + return op, slots + if rate == 1000: + mod = "DP-64QAM" + slots = 18 + op = map_modulation_to_op(mod) + return op, slots + else: + return 2, 5 + + +def consecutives(x, val): + res = [] + temp = [] + x.sort() + temp.append(x[0]) + y = 1 + for i in range(1, len(x)): + if x[i] == x[i - 1] + 1: + y += 1 + temp.append(x[i]) + else: + if y >= val: + res.extend(temp) + temp = [x[i]] + y = 1 + if i == len(x) - 1 and y >= val: + res.extend(temp) + return res + + +def combine(ls1, ls2): + temp = ls1 + for i in ls2: + if i not in ls1: + temp.append(i) + temp.sort() + return temp + + +def list_in_list(a, b): + # convert list A to numpy array + a_arr = np.array(a) + # convert list B to numpy array + b_arr = np.array(b) + + for i in range(len(b_arr)): + if np.array_equal(a_arr, b_arr[i:i + len(a_arr)]): + return True + return False + + +def reverse_link(link): + s, d = link.split('-') + r_link = "{}-{}".format(d, s) + return r_link + + +def get_slot_frequency(b, n): + if debug: + print(n) + if b == "c_slots": + return Fc + n * 12.5 + if b == "s_slots": + return Fs + n * 12.5 + if b == "l_slots": + return Fl + n * 12.5 + + +def freqency_converter(b, slots): + l = len(slots) + if debug: + print(slots) + if l % 2 == 0: + if debug: + print("pari {}".format(l)) + fx = get_slot_frequency(b, slots[int(l / 2)-1]) + if debug: + print(fx) + #GHz + # #f0 = fx + 6.25 + #MHz + f0 = int((fx + 6.25) * 1000) + else: + f0 = get_slot_frequency(b, slots[int((l + 1) / 2) - 1]) + #GHz + # #return f0, 12.5 * l + # MHz + return f0, int((12.5 * l) * 1000) + + +def readTopologyData(nodes, topology): + nodes_file = open(nodes, 'r') + topo_file = open(topology, 'r') + nodes = json.load(nodes_file) + topo = json.load(topo_file) + print(topo) + nodes_file.close() + topo_file.close() + return nodes, topo + + +def reverse_links(links): + temp_links = links.copy() + temp_links.reverse() + result = [] + for link in temp_links: + [a, b] = link.split("-") + result.append("{}-{}".format(b, a)) + return result + +def get_links_from_node(topology, node): + result = {} + for link in topology["links"]: + if "{}-".format(node) in link["optical_link"]["name"]: + result[link["optical_link"]["name"]] = link + return result + +def get_links_to_node(topology, node): + result = {} + for link in topology["links"]: + if "-{}".format(node) in link["optical_link"]["name"]: + result[link["optical_link"]["name"]] = link + return result + + +def slot_selection(c, l, s, n_slots, Nc, Nl, Ns): + # First Fit + if isinstance(n_slots, int): + slot_c = n_slots + slot_l = n_slots + slot_s = n_slots + else: + slot_c = Nc + slot_l = Nl + slot_s = Ns + if len(c) >= slot_c: + return "c_slots", c[0: slot_c] + elif len(l) >= slot_l: + return "l_slots", l[0: slot_l] + elif len(l) >= slot_s: + return "s_slots", s[0: slot_s] + else: + return None, None diff --git a/src/opticalcontroller/variables.py b/src/opticalcontroller/variables.py new file mode 100644 index 0000000000000000000000000000000000000000..cbb65200f2f0c5ef9cd490c1435fb7a9120e5d63 --- /dev/null +++ b/src/opticalcontroller/variables.py @@ -0,0 +1,32 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +debug = 1 + +Fl = 184800 +Fc = 192000 +Fs = 196200 + +Nl = 550 +Nc = 320 +#Nc = 10 +Ns = 720 + +nodes_json = 'json_files/nodes.json' +topology_json = 'json_files/tfs.json' #LAST +#topology_json = 'json_files/optical_TFSworking.json' #LAST +#topology_json = 'json_files/optical_topoTFS.json' +#topology_json = 'json_files/topo_2_links.json' + +testing = 1 diff --git a/src/service/requirements.in b/src/service/requirements.in index a10f7da7aba3027a89bdd8e9b62bc4ab90fb5e02..86720da190136c84d14e343b45cc0c97fa68ec06 100644 --- a/src/service/requirements.in +++ b/src/service/requirements.in @@ -19,3 +19,4 @@ netaddr==0.9.0 networkx==2.6.3 pydot==1.4.2 redis==4.1.2 +requests==2.27.1 diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index d7a983dc97cefd9a796ab97629ba2dbe58a71b22..f65d5b59a0d9a81c44f4d04683eb87e7773efa4a 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -34,6 +34,13 @@ from .service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory from .task_scheduler.TaskScheduler import TasksScheduler from .tools.GeodesicDistance import gps_distance +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Topology import json_topology_id +from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME +from common.proto.context_pb2 import Empty, TopologyId +from service.service.tools.OpticalTools import add_lightpath, delete_lightpath, adapt_reply, get_device_name_from_uuid, get_optical_band + + LOGGER = logging.getLogger(__name__) METRICS_POOL = MetricsPool('Service', 'RPC') @@ -242,16 +249,74 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): pathcomp_request = PathCompRequest() pathcomp_request.services.append(service_with_uuids) # pylint: disable=no-member - if num_disjoint_paths is None or num_disjoint_paths in {0, 1}: + if service.service_type == ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY: + context_id_x = json_context_id(DEFAULT_CONTEXT_NAME) + topology_id_x = json_topology_id( + DEFAULT_TOPOLOGY_NAME, context_id_x) + topology_details = context_client.GetTopologyDetails( + TopologyId(**topology_id_x)) + # devices = get_devices_in_topology(context_client, TopologyId(**topology_id_x), ContextId(**context_id_x)) + devices = topology_details.devices + context_uuid_x = topology_details.topology_id.context_id.context_uuid.uuid + topology_uuid_x = topology_details.topology_id.topology_uuid.uuid + devs = [] + ports = [] + for endpoint_id in service.service_endpoint_ids: + devs.append(endpoint_id.device_id.device_uuid.uuid) + ports.append(endpoint_id.endpoint_uuid.uuid) + src = devs[0] + dst = devs[1] + bidir = None + ob_band = None + bitrate = 100 + for constraint in service.service_constraints: + if "bandwidth" in constraint.custom.constraint_type: + bitrate = int(float(constraint.custom.constraint_value)) + elif "bidirectionality" in constraint.custom.constraint_type: + bidir = int(constraint.custom.constraint_value) + elif "optical-band-width" in constraint.custom.constraint_type: + ob_band = int(constraint.custom.constraint_value) + + # to get the reply form the optical module + reply_txt = add_lightpath(src, dst, bitrate, bidir, ob_band) + + # reply with 2 transponders and 2 roadms + reply_json = json.loads(reply_txt) + optical_band_txt = "" + if "new_optical_band" in reply_json.keys(): + if reply_json["new_optical_band"] == 1: + if reply_json["parent_opt_band"]: + if "parent_opt_band" in reply_json.keys(): + parent_ob = reply_json["parent_opt_band"] + LOGGER.debug('Parent optical-band={}'.format(parent_ob)) + optical_band_txt = get_optical_band(parent_ob) + LOGGER.info('optical-band details={}'.format(optical_band_txt)) + else: + LOGGER.debug('expected optical band not found') + else: + LOGGER.debug('expected optical band not found') + else: + LOGGER.debug('Using existing optical band') + else: + LOGGER.debug('Using existing optical band') + if reply_txt is not None: + optical_reply = adapt_reply( + devices, _service, reply_json, context_uuid_x, topology_uuid_x, optical_band_txt + ) + LOGGER.info('optical_reply={:s}'.format( + grpc_message_to_json_string(optical_reply))) + + tasks_scheduler.compose_from_pathcompreply( + optical_reply, is_delete=False) + else: + if num_disjoint_paths is None or num_disjoint_paths in {0, 1} : pathcomp_request.shortest_path.Clear() # pylint: disable=no-member else: pathcomp_request.k_disjoint_path.num_disjoint = num_disjoint_paths # pylint: disable=no-member - LOGGER.debug('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) pathcomp = PathCompClient() pathcomp_reply = pathcomp.Compute(pathcomp_request) pathcomp.close() - LOGGER.debug('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) # Feed TaskScheduler with this path computation reply. TaskScheduler identifies inter-dependencies among # the services and connections retrieved and produces a schedule of tasks (an ordered list of tasks to be @@ -280,6 +345,29 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): context_client.RemoveService(request) return Empty() + if service.service_type == ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY: + devs = [] + + context_id_x = json_context_id(DEFAULT_CONTEXT_NAME) + topology_id_x = json_topology_id( + DEFAULT_TOPOLOGY_NAME, context_id_x) + topology_details = context_client.GetTopologyDetails( + TopologyId(**topology_id_x)) + devices = topology_details.devices + for endpoint_id in service.service_endpoint_ids: + devs.append(endpoint_id.device_id.device_uuid.uuid) + src = get_device_name_from_uuid(devices, devs[0]) + dst = get_device_name_from_uuid(devices, devs[1]) + + bitrate = int( + float(service.service_constraints[0].custom.constraint_value)) + if len(service.service_config.config_rules) > 0: + c_rules_dict = json.loads( + service.service_config.config_rules[0].custom.resource_value) + if ("flow_id" in c_rules_dict): + flow_id = c_rules_dict["flow_id"] + reply = delete_lightpath(flow_id, src, dst, bitrate) + # Normal service # Feed TaskScheduler with this service and the sub-services and sub-connections related to this service. # TaskScheduler identifies inter-dependencies among them and produces a schedule of tasks (an ordered list of diff --git a/src/service/service/__main__.py b/src/service/service/__main__.py index f2b6e38d6181a0c56b4f1dfca3116717d1400ced..edd4d8f9973e20d0df6e2d99ba8164d5d68f068d 100644 --- a/src/service/service/__main__.py +++ b/src/service/service/__main__.py @@ -17,7 +17,8 @@ from prometheus_client import start_http_server from common.Constants import ServiceNameEnum from common.Settings import ( ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, get_env_var_name, get_log_level, get_metrics_port, - wait_for_environment_variables) + wait_for_environment_variables +) from .ServiceService import ServiceService from .service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory from .service_handlers import SERVICE_HANDLERS diff --git a/src/service/service/service_handler_api/FilterFields.py b/src/service/service/service_handler_api/FilterFields.py index e771e24f17b2ea97c61158b65cb62c87ee9f37c5..633f41b63c71727d9ead0ee57b79002dc3a6cd97 100644 --- a/src/service/service/service_handler_api/FilterFields.py +++ b/src/service/service/service_handler_api/FilterFields.py @@ -26,6 +26,7 @@ SERVICE_TYPE_VALUES = { ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, ServiceTypeEnum.SERVICETYPE_TE, ServiceTypeEnum.SERVICETYPE_E2E, + ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY } DEVICE_DRIVER_VALUES = { @@ -40,6 +41,7 @@ DEVICE_DRIVER_VALUES = { DeviceDriverEnum.DEVICEDRIVER_GNMI_OPENCONFIG, DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE, DeviceDriverEnum.DEVICEDRIVER_IETF_ACTN, + DeviceDriverEnum.DEVICEDRIVER_OC } # Map allowed filter fields to allowed values per Filter field. If no restriction (free text) None is specified diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index eaf8f715aeff435b67bce77928e726daeb4729e2..f0628110fbd5f2b15308f05c9061c699a09dbcff 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -26,6 +26,7 @@ from .p4.p4_service_handler import P4ServiceHandler from .tapi_tapi.TapiServiceHandler import TapiServiceHandler from .tapi_xr.TapiXrServiceHandler import TapiXrServiceHandler from .e2e_orch.E2EOrchestratorServiceHandler import E2EOrchestratorServiceHandler +from .oc.OCServiceHandler import OCServiceHandler SERVICE_HANDLERS = [ (L2NMEmulatedServiceHandler, [ @@ -100,4 +101,10 @@ SERVICE_HANDLERS = [ FilterFieldEnum.DEVICE_DRIVER : [DeviceDriverEnum.DEVICEDRIVER_FLEXSCALE], } ]), + (OCServiceHandler, [ + { + FilterFieldEnum.SERVICE_TYPE : ServiceTypeEnum.SERVICETYPE_OPTICAL_CONNECTIVITY, + FilterFieldEnum.DEVICE_DRIVER : DeviceDriverEnum.DEVICEDRIVER_OC, + } + ]) ] diff --git a/src/service/service/service_handlers/oc/ConfigRules.py b/src/service/service/service_handlers/oc/ConfigRules.py new file mode 100644 index 0000000000000000000000000000000000000000..f4a46112e778bd01aa76322384d8adee942aaa5b --- /dev/null +++ b/src/service/service/service_handlers/oc/ConfigRules.py @@ -0,0 +1,255 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +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, endpoint_name : str, + service_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + if service_settings is None: return [] + if endpoint_settings is None: return [] + + json_settings : Dict = service_settings.value + json_endpoint_settings : Dict = 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_name, 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_name), { + 'name': endpoint_name, 'description': network_interface_desc, 'mtu': mtu, + }), + json_config_rule_set( + '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_name, sub_interface_index), { + 'name': endpoint_name, '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_name, + '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, endpoint_name : str, + service_settings : TreeNode, endpoint_settings : TreeNode +) -> List[Dict]: + + if service_settings is None: return [] + if endpoint_settings is None: return [] + + json_settings : Dict = service_settings.value + json_endpoint_settings : Dict = 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_name, 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_name, sub_interface_index), { + 'name': endpoint_name, 'index': sub_interface_index, + }), + json_config_rule_delete( + '/interface[{:s}]'.format(endpoint_name), { + 'name': endpoint_name, + }), + 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/oc/OCServiceHandler.py b/src/service/service/service_handlers/oc/OCServiceHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..6359f2a09e53cab1002acd55d477aeb2d9b393b0 --- /dev/null +++ b/src/service/service/service_handlers/oc/OCServiceHandler.py @@ -0,0 +1,169 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json, logging +from typing import Any, 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.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 +from .ConfigRules import setup_config_rules, teardown_config_rules +from .OCTools import convert_endpoints_to_flows, handle_flows_names + +LOGGER = logging.getLogger(__name__) + +METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'oc'}) + +class OCServiceHandler(_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) + + @metered_subclass_method(METRICS_POOL) + 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 [] + is_opticalband =False + #service_uuid = self.__service.service_id.service_uuid.uuid + settings=None + if self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)): + is_opticalband=True + settings = self.__settings_handler.get('/settings-ob_{}'.format(connection_uuid)) + else: + settings = self.__settings_handler.get('/settings') + + + # settings = self.__settings_handler.get('/settings') + + #flow is the new variable that stores input-output relationship + + flows = convert_endpoints_to_flows(endpoints) + #handled_flows=handle_flows_names(flows=flows,task_executor=self.__task_executor) + + #LOGGER.info("Handled Flows %s",handled_flows) + + results = [] + #new cycle for setting optical devices + for device_uuid in flows.keys(): + try: + dev_flows = flows[device_uuid] + device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + + + if (settings): + + self.__task_executor.configure_optical_device(device_obj, settings, dev_flows, is_opticalband) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to configure Device({:s})'.format(str(device_uuid))) + results.append(e) + + return results + + @metered_subclass_method(METRICS_POOL) + 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.__service.service_id.service_uuid.uuid + settings = self.__settings_handler.get('/settings') + + results = [] + for endpoint in endpoints: + try: + device_uuid, endpoint_uuid = get_device_endpoint_uuids(endpoint) + + device_obj = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) + endpoint_settings = self.__settings_handler.get_endpoint_settings(device_obj, endpoint_obj) + endpoint_name = endpoint_obj.name + + json_config_rules = teardown_config_rules( + service_uuid, connection_uuid, device_uuid, endpoint_uuid, endpoint_name, + settings, endpoint_settings) + + if len(json_config_rules) > 0: + del device_obj.device_config.config_rules[:] + for json_config_rule in json_config_rules: + device_obj.device_config.config_rules.append(ConfigRule(**json_config_rule)) + self.__task_executor.configure_device(device_obj) + + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint))) + results.append(e) + + return results + + @metered_subclass_method(METRICS_POOL) + def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + @metered_subclass_method(METRICS_POOL) + def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + @metered_subclass_method(METRICS_POOL) + def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + resource_value = json.loads(resource[1]) + self.__settings_handler.set(resource[0], resource_value) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource))) + results.append(e) + + return results + + @metered_subclass_method(METRICS_POOL) + def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + self.__settings_handler.delete(resource[0]) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource))) + results.append(e) + + return results diff --git a/src/service/service/service_handlers/oc/OCTools.py b/src/service/service/service_handlers/oc/OCTools.py new file mode 100644 index 0000000000000000000000000000000000000000..2b202a8a9ce2183342fc23f3563cc16d81abcd5d --- /dev/null +++ b/src/service/service/service_handlers/oc/OCTools.py @@ -0,0 +1,142 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from service.service.service_handler_api.Tools import get_device_endpoint_uuids, get_endpoint_matching +from typing import Dict, Any, List, Optional, Tuple +import logging +from common.proto.context_pb2 import ConfigRule, DeviceId, Service +from common.tools.object_factory.Device import json_device_id + +log = logging.getLogger(__name__) + +#def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])->Dict[str: List[Tuple[str, str]]]: + +def convert_endpoints_to_flows(endpoints : List[Tuple[str, str, Optional[str]]])->Dict: + #entries = List[Tuple[str, str, str, Optional[str]]] + #entries = Dict[str: List[Tuple[str, str]]] + entries = {} + #tuple is in, out + #end = len(endpoints) if isinstance(endpoints,list) else 0 + end = len(endpoints) + i = 0 + bidir = 0 + log.debug("end={}".format(end)) + while(i < end): + endpoint = endpoints[i] + device_uuid, endpoint_uuid = endpoint[0:2] + log.debug("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid)) + if device_uuid not in entries.keys(): + entries[device_uuid] = [] + if i == 0: + entry_tuple = "0", endpoint_uuid + entries[device_uuid].append(entry_tuple) + next_endpoint = endpoints[i+1] + next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] + if next_device_uuid == device_uuid: + bidir = 1 + log.debug("connection is bidirectional") + entry_tuple = next_endpoint_uuid, "0" + entries[device_uuid].append(entry_tuple) + i = i + 1 + else: + log.debug("connection is unidirectional") + else: + if not bidir: + if i == end-1: + #is the last node + entry_tuple = endpoint_uuid, "0" + entries[device_uuid].append(entry_tuple) + else: + #it is a transit node + next_endpoint = endpoints[i+1] + next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] + if next_device_uuid == device_uuid: + entry_tuple = endpoint_uuid, next_endpoint_uuid + entries[device_uuid].append(entry_tuple) + i = i + 1 + log.debug("current OCTools step {}, {}, {}".format(i, next_device_uuid, device_uuid)) + else: + log.debug("ERROR in unidirectional connection 4") + return {} + if bidir: + log.debug("Ocheck i {}, {}, {}".format(i, i+1, end-1)) + if i + 1 == end-1: + log.debug("current OCTools step {}, {}, {}".format(i, device_uuid, endpoint_uuid)) + #is the last node + entry_tuple = endpoint_uuid, "0" + entries[device_uuid].append(entry_tuple) + next_endpoint = endpoints[i+1] + log.debug("OCTools i+1 step {}, {}, {}".format(i+1, next_device_uuid, device_uuid)) + + next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] + if next_device_uuid == device_uuid: + entry_tuple = "0", next_endpoint_uuid + entries[device_uuid].append(entry_tuple) + i = i + 1 + else: + log.debug("ERROR in bidirectional connection 2") + return entries + else: + log.debug("OCTools i+1+2+3 step {}, {}, {}".format(i+1, next_device_uuid, device_uuid)) + #i+1 + next_endpoint = endpoints[i+1] + next_device_uuid, next_endpoint_uuid = next_endpoint[0:2] + if next_device_uuid == device_uuid: + entry_tuple = endpoint_uuid, next_endpoint_uuid + entries[device_uuid].append(entry_tuple) + else: + log.debug("ERROR in bidirectional connection 3") + log.debug("{}, {}, {}".format(i, next_device_uuid, device_uuid)) + return entries + #i+2 + next_2_endpoint = endpoints[i+2] + next_2_device_uuid, next_2_endpoint_uuid = next_2_endpoint[0:2] + #i+3 + next_3_endpoint = endpoints[i+3] + next_3_device_uuid, next_3_endpoint_uuid = next_3_endpoint[0:2] + if next_2_device_uuid == next_3_device_uuid and next_3_device_uuid == device_uuid: + entry_tuple = next_2_endpoint_uuid, next_3_endpoint_uuid + entries[device_uuid].append(entry_tuple) + i = i + 3 + else: + log.debug("ERROR in bidirection connection 4") + return {} + i = i + 1 + return entries + + +def get_device_endpint_name (endpoint_uuid:str,device_uuid:str,task_executor)->Tuple: + device_obj = task_executor.get_device(DeviceId(**json_device_id(device_uuid))) + endpoint_obj = get_endpoint_matching(device_obj, endpoint_uuid) + endpoint_name = endpoint_obj.name + return (device_obj.name, endpoint_name) + +def handle_flows_names (task_executor,flows:dict)->Dict : + new_flows={} + for index,( device_uuid_key , device_endpoints_list) in enumerate(flows.items()): + for endpoint_tupple in device_endpoints_list: + source_port=None + destination_port=None + device_name="" + source_endpoint,destination_endpoint =endpoint_tupple + if (source_endpoint !='0'): + if get_device_endpint_name(source_endpoint,device_uuid_key,task_executor) is not None: + device_name,source_port=get_device_endpint_name(source_endpoint,device_uuid_key,task_executor) + if (destination_endpoint !='0'): + if get_device_endpint_name(destination_endpoint,device_uuid_key,task_executor) is not None: + device_name,destination_port=get_device_endpint_name(destination_endpoint,device_uuid_key,task_executor) + if (device_name not in new_flows): + new_flows[device_name]=[] + new_flows[device_name].append((source_port,destination_port)) + return new_flows diff --git a/src/service/service/service_handlers/oc/__init__.py b/src/service/service/service_handlers/oc/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612 --- /dev/null +++ b/src/service/service/service_handlers/oc/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index ae0f1be7da291a5dc025641cb606f7a7706059ca..b9715aae637bb8845077bfc2b8b9b9d1bb030645 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -12,11 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging #, json +import json, logging from enum import Enum from typing import TYPE_CHECKING, Any, Dict, Optional, Union from common.method_wrappers.ServiceExceptions import NotFoundException -from common.proto.context_pb2 import Connection, ConnectionId, Device, DeviceDriverEnum, DeviceId, Service, ServiceId +from common.proto.context_pb2 import ( + Connection, ConnectionId, Device, DeviceDriverEnum, DeviceId, Service, ServiceId, + OpticalConfig, OpticalConfigId +) from common.tools.context_queries.Connection import get_connection_by_id from common.tools.context_queries.Device import get_device from common.tools.context_queries.Service import get_service_by_id @@ -25,7 +28,8 @@ from common.tools.object_factory.Device import json_device_id from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from service.service.service_handler_api.Exceptions import ( - UnsatisfiedFilterException, UnsupportedFilterFieldException, UnsupportedFilterFieldValueException) + UnsatisfiedFilterException, UnsupportedFilterFieldException, UnsupportedFilterFieldValueException +) from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory, get_service_handler_class from service.service.tools.ObjectKeys import get_connection_key, get_device_key, get_service_key @@ -108,9 +112,33 @@ class TaskExecutor: return device def configure_device(self, device : Device) -> None: + self._context_client.SelectOpticalConfig() device_key = get_device_key(device.device_id) self._device_client.ConfigureDevice(device) self._store_grpc_object(CacheableObjectType.DEVICE, device_key, device) + + # New function Andrea for Optical Devices + def configure_optical_device(self, device : Device, settings : str, flows : list, is_opticalband : bool): + device_key = get_device_key(device.device_id) + myid = OpticalConfigId() + myid.opticalconfig_uuid = device.device_id.device_uuid.uuid + opticalconfig = OpticalConfig() + setting = settings.value if settings else "" + + new_config = {} + try: + result = self._context_client.SelectOpticalConfig(myid) + new_config = json.loads(result.config) + if result is not None : + new_config["new_config"] = setting + new_config["is_opticalband"] = is_opticalband + new_config["flow"] = flows + result.config = str(new_config) + opticalconfig.CopyFrom(result) + self._device_client.ConfigureOpticalDevice(opticalconfig) + self._store_grpc_object(CacheableObjectType.DEVICE, device_key, device) + except Exception as e: + LOGGER.info("error in config my config %s",e) def get_device_controller(self, device : Device) -> Optional[Device]: #json_controller = None diff --git a/src/service/service/tools/OpticalTools.py b/src/service/service/tools/OpticalTools.py new file mode 100644 index 0000000000000000000000000000000000000000..8b3e3153beb53063b17ae7998e8a7d49f8abd64e --- /dev/null +++ b/src/service/service/tools/OpticalTools.py @@ -0,0 +1,300 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import json +import requests +import uuid +from common.Constants import * +from typing import Dict, List +from common.proto.context_pb2 import( + Device, DeviceId, Service, Connection, EndPointId, TopologyId, ContextId, Uuid, + ConfigRule, ConfigActionEnum, ConfigRule_Custom +) +from common.proto.pathcomp_pb2 import PathCompReply +from typing import Dict, List, Optional, Tuple +from common.Constants import ServiceNameEnum +from common.Settings import ( + ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, find_environment_variables, get_env_var_name +) +from context.service.database.uuids.EndPoint import endpoint_get_uuid +from service.service.tools.replies import reply_uni_txt, optical_band_uni_txt, reply_bid_txt, optical_band_bid_txt + +log = logging.getLogger(__name__) + +testing = False + +VAR_NAME_OPTICAL_CONTROLLER_HOST = get_env_var_name(ServiceNameEnum.OPTICALCONTROLLER, ENVVAR_SUFIX_SERVICE_HOST) +VAR_NAME_OPTICAL_CONTROLLER_PORT = get_env_var_name(ServiceNameEnum.OPTICALCONTROLLER, ENVVAR_SUFIX_SERVICE_PORT_GRPC) + +opticalcontrollers_url = find_environment_variables([ + VAR_NAME_OPTICAL_CONTROLLER_HOST, + VAR_NAME_OPTICAL_CONTROLLER_PORT, +]) +OPTICAL_IP = opticalcontrollers_url.get(VAR_NAME_OPTICAL_CONTROLLER_HOST) +OPTICAL_PORT = opticalcontrollers_url.get(VAR_NAME_OPTICAL_CONTROLLER_PORT) +log.info(str(OPTICAL_IP), str(OPTICAL_PORT)) + +def get_uuids_from_names(devices: List[Device], device_name: str, port_name: str): + device_uuid = "" + port_uuid = "" + for device in devices: + if device.name == device_name: + device_uuid = device.device_id.device_uuid.uuid + for ep in device.device_endpoints: + if ep.name == port_name: + port_uuid = ep.endpoint_id.endpoint_uuid.uuid + return device_uuid, port_uuid + return "", "" + + +def get_names_from_uuids(devices: List[Device], device_uuid: str, port_uuid: str): + device_name = "" + port_name = "" + for device in devices: + if device.device_id.device_uuid.uuid == device_uuid: + device_name = device.name + for ep in device.device_endpoints: + if ep.endpoint_id.endpoint_uuid.uuid == port_uuid: + port_name = ep.name + return device_name, port_name + return "", "" + +def get_device_name_from_uuid(devices: List[Device], device_uuid: str): + device_name = "" + + for device in devices: + if device.device_id.device_uuid.uuid == device_uuid: + device_name = device.name + return device_name + return "" + + +def add_lightpath(src, dst, bitrate, bidir, ob_band) -> str: + if not testing: + urlx = "" + headers = {"Content-Type": "application/json"} + if ob_band is None: + if bidir is None: + bidir = 1 + urlx = "http://{}:{}/OpticalTFS/AddFlexLightpath/{}/{}/{}/{}".format(OPTICAL_IP, OPTICAL_PORT, src, dst, bitrate, bidir) + else: + if bidir is None: + bidir = 1 + urlx = "http://{}:{}/OpticalTFS/AddFlexLightpath/{}/{}/{}/{}/{}".format(OPTICAL_IP, OPTICAL_PORT, src, dst, bitrate, bidir, ob_band) + r = requests.put(urlx, headers=headers) + reply = r.text + return reply + else: + if bidir is not None: + if bidir == 0: + return reply_uni_txt + return reply_bid_txt + + +def get_optical_band(idx) -> str: + if not testing: + urlx = "http://{}:{}/OpticalTFS/GetOpticalBand/{}".format(OPTICAL_IP, OPTICAL_PORT, idx) + headers = {"Content-Type": "application/json"} + r = requests.get(urlx, headers=headers) + reply = r.text + return reply + else: + if str(idx) == "1": + return optical_band_bid_txt + else: + return optical_band_uni_txt + + +def delete_lightpath(flow_id, src, dst, bitrate) -> str: + reply = "200" + if not testing: + urlx = "http://{}:{}/OpticalTFS/DelLightpath/{}/{}/{}/{}".format(OPTICAL_IP, OPTICAL_PORT, flow_id, src, dst, bitrate) + + headers = {"Content-Type": "application/json"} + r = requests.delete(urlx, headers=headers) + reply = r.text + return reply + + +def get_lightpaths() -> str: + urlx = "http://{}:{}/OpticalTFS/GetLightpaths".format(OPTICAL_IP, OPTICAL_PORT) + + headers = {"Content-Type": "application/json"} + r = requests.get(urlx, headers=headers) + reply = r.text + return reply + + +def adapt_reply(devices, service, reply_json, context_id, topology_id, optical_band_txt) -> PathCompReply: + opt_reply = PathCompReply() + topo = TopologyId(context_id=ContextId(context_uuid=Uuid(uuid=context_id)),topology_uuid=Uuid(uuid=topology_id)) + #add optical band connection first + rules_ob= [] + ob_id = 0 + connection_ob=None + if optical_band_txt != "": + ob_json = json.loads(optical_band_txt) + ob = ob_json + connection_ob = add_connection_to_reply(opt_reply) + uuuid_x = str(uuid.uuid4()) + connection_ob.connection_id.connection_uuid.uuid = uuuid_x + connection_ob.service_id.CopyFrom(service.service_id) + + ob_id = ob["optical_band_id"] + obt = ob["band_type"] + if obt == "l_slots": + band_type = "L_BAND" + elif obt == "s_slots": + band_type = "S_BAND" + else: + band_type = "C_BAND" + + freq = ob["freq"] + bx = ob["band"] + lf = int(int(freq)-int(bx/2)) + uf = int(int(freq)+int(bx/2)) + val_ob = {"band_type": band_type, "low-freq": lf, "up-freq": uf, "frequency": freq, "band": bx, "ob_id": ob_id} + rules_ob.append(ConfigRule_Custom(resource_key="/settings-ob_{}".format(uuuid_x), resource_value=json.dumps(val_ob))) + bidir_ob = ob["bidir"] + for devxb in ob["flows"].keys(): + log.debug("optical-band device {}".format(devxb)) + in_end_point_b = "0" + out_end_point_b = "0" + in_end_point_f = ob["flows"][devxb]["f"]["in"] + out_end_point_f = ob["flows"][devxb]["f"]["out"] + log.debug("optical-band ports {}, {}".format(in_end_point_f, out_end_point_f)) + if bidir_ob: + in_end_point_b = ob["flows"][devxb]["b"]["in"] + out_end_point_b = ob["flows"][devxb]["b"]["out"] + log.debug("optical-band ports {}, {}".format(in_end_point_b, out_end_point_b)) + #if (in_end_point_f == "0" or out_end_point_f == "0") and (in_end_point_b == "0" or out_end_point_b == "0"): + if in_end_point_f != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_f) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, in_end_point_f)) + + if out_end_point_f != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_f) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, out_end_point_f)) + if in_end_point_b != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, in_end_point_b) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, in_end_point_b)) + if out_end_point_b != "0": + d_ob, p_ob = get_uuids_from_names(devices, devxb, out_end_point_b) + if d_ob != "" and p_ob != "": + end_point_b = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d_ob)), endpoint_uuid=Uuid(uuid=p_ob)) + connection_ob.path_hops_endpoint_ids.add().CopyFrom(end_point_b) + else: + log.info("no map device port for device {} port {}".format(devxb, out_end_point_b)) + log.debug("optical-band connection {}".format(connection_ob)) + r = reply_json + bidir_f = r["bidir"] + connection_f = add_connection_to_reply(opt_reply) + connection_f.connection_id.connection_uuid.uuid = str(uuid.uuid4()) + connection_f.service_id.CopyFrom(service.service_id) + for devx in r["flows"].keys(): + log.debug("lightpath device {}".format(devx)) + in_end_point_b = "0" + out_end_point_b = "0" + + in_end_point_f = r["flows"][devx]["f"]["in"] + out_end_point_f = r["flows"][devx]["f"]["out"] + log.debug("lightpath ports {}, {}".format(in_end_point_f, out_end_point_f)) + if bidir_f: + in_end_point_b = r["flows"][devx]["b"]["in"] + out_end_point_b = r["flows"][devx]["b"]["out"] + log.debug("lightpath ports {}, {}".format(in_end_point_b, out_end_point_b)) + if in_end_point_f != "0": + d, p = get_uuids_from_names(devices, devx, in_end_point_f) + if d != "" and p != "": + end_point = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d)), endpoint_uuid=Uuid(uuid=p)) + connection_f.path_hops_endpoint_ids.add().CopyFrom(end_point) + else: + log.info("no map device port for device {} port {}".format(devx, in_end_point_f)) + if out_end_point_f != "0": + d, p = get_uuids_from_names(devices, devx, out_end_point_f) + if d != "" and p != "": + end_point = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d)), endpoint_uuid=Uuid(uuid=p)) + connection_f.path_hops_endpoint_ids.add().CopyFrom(end_point) + else: + log.info("no map device port for device {} port {}".format(devx, out_end_point_f)) + if in_end_point_b != "0": + d, p = get_uuids_from_names(devices, devx, in_end_point_b) + if d != "" and p != "": + end_point = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d)), endpoint_uuid=Uuid(uuid=p)) + connection_f.path_hops_endpoint_ids.add().CopyFrom(end_point) + else: + log.info("no map device port for device {} port {}".format(devx, in_end_point_b)) + if out_end_point_b != "0": + d, p = get_uuids_from_names(devices, devx, out_end_point_b) + if d != "" and p != "": + end_point = EndPointId(topology_id=topo, device_id=DeviceId(device_uuid=Uuid(uuid=d)), endpoint_uuid=Uuid(uuid=p)) + connection_f.path_hops_endpoint_ids.add().CopyFrom(end_point) + else: + log.info("no map device port for device {} port {}".format(devx, out_end_point_b)) + #check that list of endpoints is not empty + if connection_ob is not None and len(connection_ob.path_hops_endpoint_ids) == 0: + log.debug("deleting empty optical-band connection") + opt_reply.connections.remove(connection_ob) + + #inizialize custom optical parameters + band = r["band"] if "band" in r else None + op_mode = r["op-mode"] if "op-mode" in r else None + frequency = r["freq"] if "freq" in r else None + flow_id = r["flow_id"] if "flow_id" in r else None + r_type = r["band_type"] if "band_type" in r else None + if r_type == "l_slots": + band_type = "L_BAND" + elif r_type == "s_slots": + band_type = "S_BAND" + else: + band_type = "C_BAND" + + if ob_id != 0: + val = {"frequency": frequency, "operational-mode": op_mode, "band": band, "flow_id": flow_id, "ob_id": ob_id, "band_type": band_type,} + else: + val = {"frequency": frequency, "operational-mode": op_mode, "band": band, "flow_id": flow_id, "band_type": band_type,} + custom_rule = ConfigRule_Custom(resource_key="/settings", resource_value=json.dumps(val)) + rule = ConfigRule(action=ConfigActionEnum.CONFIGACTION_SET, custom=custom_rule) + service.service_config.config_rules.add().CopyFrom(rule) + + if len(rules_ob) > 0: + for rulex in rules_ob: + rule_ob = ConfigRule(action=ConfigActionEnum.CONFIGACTION_SET, custom=rulex) + service.service_config.config_rules.add().CopyFrom(rule_ob) + + opt_reply.services.add().CopyFrom(service) + + return opt_reply + +def add_service_to_reply(reply : PathCompReply, service : Service)-> Service: + service_x = reply.services.add() + service_x.CopyFrom(service) + return service_x + +def add_connection_to_reply(reply : PathCompReply)-> Connection: + conn = reply.connections.add() + return conn diff --git a/src/service/service/tools/replies.py b/src/service/service/tools/replies.py new file mode 100644 index 0000000000000000000000000000000000000000..5fcb393e9d8a5eaaeccc11a8c1dc8e4485b8cbc9 --- /dev/null +++ b/src/service/service/tools/replies.py @@ -0,0 +1,321 @@ +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +reply_bid_txt = """ +{ + "flow_id": 1, + "src": "T1", + "dst": "T2", + "bitrate": 100, + "bidir": 1, + "flows": { + "T1": { + "f": { + "in": "0", + "out": "1" + }, + "b": { + "in": "1", + "out": "0" + } + }, + "R1": { + "f": { + "in": "12", + "out": "3" + }, + "b": { + "in": "13", + "out": "2" + } + }, + "R2": { + "f": { + "in": "14", + "out": "5" + }, + "b": { + "in": "15", + "out": "4" + } + }, + "T2": { + "f": { + "in": "6", + "out": "0" + }, + "b": { + "in": "0", + "out": "6" + } + } + }, + "band_type": "c_slots", + "slots": [ + 1, + 2, + 3, + 4 + ], + "fiber_forward": { + "T1-R1": "M1", + "R2-T2": "S1" + }, + "fiber_backward": { + "R1-T1": "S1", + "T2-R2": "M1" + }, + "op-mode": 1, + "n_slots": 4, + "links": [ + "T1-R1", + "R2-T2" + ], + "path": [ + "R1", + "R2" + ], + "band": 50000, + "freq": 192031250, + "is_active": true, + "parent_opt_band": 1, + "new_optical_band": 1 +} + """ + +optical_band_bid_txt = """ +{ + "optical_band_id": 1, + "bidir": 1, + "src": "R1", + "dst": "R2", + "flows": { + "R1": { + "f": { + "in": "0", + "out": "3" + }, + "b": { + "in": "13", + "out": "0" + } + }, + "R2": { + "f": { + "in": "14", + "out": "0" + }, + "b": { + "in": "0", + "out": "4" + } + } + }, + "band_type": "c_slots", + "fiber_forward": { + "R1-R2": "d1-1" + }, + "fiber_backward": { + "R2-R1": "d1-1" + }, + "op-mode": 0, + "n_slots": 16, + "links": [ + "R1-R2" + ], + "path": [ + "R1", + "R2" + ], + "band": 200000, + "freq": 192106250, + "is_active": true, + "src_port": "101", + "dst_port": "201", + "rev_dst_port": "201", + "rev_src_port": "101", + "c_slots": [ + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16 + ], + "served_lightpaths": [ + 1 + ] +} + """ + +reply_uni_txt = """ +{ + "flow_id": 2, + "src": "T1", + "dst": "T2", + "bitrate": 100, + "bidir": 0, + "flows": { + "T1": { + "f": { + "in": "0", + "out": "1" + }, + "b": { + "in": "1", + "out": "0" + } + }, + "R1": { + "f": { + "in": "12", + "out": "3" + }, + "b": { + "in": "13", + "out": "2" + } + }, + "R2": { + "f": { + "in": "14", + "out": "5" + }, + "b": { + "in": "15", + "out": "4" + } + }, + "T2": { + "f": { + "in": "6", + "out": "0" + }, + "b": { + "in": "0", + "out": "6" + } + } + }, + "band_type": "c_slots", + "slots": [ + 1, + 2, + 3, + 4 + ], + "fiber_forward": { + "T1-R1": "M1", + "R2-T2": "S1" + }, + "fiber_backward": { + "R1-T1": "S1", + "T2-R2": "M1" + }, + "op-mode": 1, + "n_slots": 4, + "links": [ + "T1-R1", + "R2-T2" + ], + "path": [ + "R1", + "R2" + ], + "band": 50000, + "freq": 192031250, + "is_active": true, + "parent_opt_band": 2, + "new_optical_band": 1 +} + """ + +optical_band_uni_txt = """ +{ + "optical_band_id": 2, + "bidir": 0, + "src": "R1", + "dst": "R2", + "flows": { + "R1": { + "f": { + "in": "0", + "out": "3" + }, + "b": { + "in": "13", + "out": "0" + } + }, + "R2": { + "f": { + "in": "14", + "out": "0" + }, + "b": { + "in": "0", + "out": "4" + } + } + }, + "band_type": "c_slots", + "fiber_forward": { + "R1-R2": "d1-1" + }, + "fiber_backward": { + "R2-R1": "d1-1" + }, + "op-mode": 0, + "n_slots": 16, + "links": [ + "R1-R2" + ], + "path": [ + "R1", + "R2" + ], + "band": 200000, + "freq": 192106250, + "is_active": true, + "src_port": "101", + "dst_port": "201", + "rev_dst_port": "201", + "rev_src_port": "101", + "c_slots": [ + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16 + ], + "served_lightpaths": [ + 2 + ] +} + """ + diff --git a/src/tests/ofc24/1.context.json b/src/tests/ofc24/1.context.json new file mode 100755 index 0000000000000000000000000000000000000000..36b3c44fd61fdec9d208a82a11d5a16c3671d004 --- /dev/null +++ b/src/tests/ofc24/1.context.json @@ -0,0 +1,19 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [], + "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "admin"} + }, + "device_ids": [], + "link_ids": [] + } + ] +} diff --git a/src/tests/ofc24/2.device1.json b/src/tests/ofc24/2.device1.json new file mode 100755 index 0000000000000000000000000000000000000000..3e31f31eb84630415f96b6af1e6ae5d34bdb1c89 --- /dev/null +++ b/src/tests/ofc24/2.device1.json @@ -0,0 +1,91 @@ +{ + "devices": [ + { + "device_id": { + "device_uuid": { + "uuid": "T1" + } + }, + "device_type": "optical-transponder", + "device_drivers": [ + 11 + ], + "device_endpoints": [ + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "T1" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "1" + } + } + } + ], + "device_operational_status": 1, + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "10.0.2.15" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "2023" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "username": "admin", + "password": "admin", + "force_running": false, + "hostkey_verify": false, + "look_for_keys": false, + "allow_agent": false, + "commit_per_rule": false, + "device_params": { + "name": "default" + }, + "manager_params": { + "timeout": 120 + }, + "endpoints": [ + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "1" + } + ] + } + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc24/3.device2.json b/src/tests/ofc24/3.device2.json new file mode 100755 index 0000000000000000000000000000000000000000..812affa7b8540b67f83d6f3c9bb9b5442c44fd0d --- /dev/null +++ b/src/tests/ofc24/3.device2.json @@ -0,0 +1,91 @@ +{ + "devices": [ + { + "device_id": { + "device_uuid": { + "uuid": "T2" + } + }, + "device_type": "optical-transponder", + "device_drivers": [ + 11 + ], + "device_endpoints": [ + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "T2" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "6" + } + } + } + ], + "device_operational_status": 1, + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "10.0.2.15" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "2024" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "username": "admin", + "password": "admin", + "force_running": false, + "hostkey_verify": false, + "look_for_keys": false, + "allow_agent": false, + "commit_per_rule": false, + "device_params": { + "name": "default" + }, + "manager_params": { + "timeout": 120 + }, + "endpoints": [ + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "6" + } + ] + } + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc24/4.device3_R1.json b/src/tests/ofc24/4.device3_R1.json new file mode 100755 index 0000000000000000000000000000000000000000..3a57ba79cd2ff8aa6d4b666ac382932ade2f20e0 --- /dev/null +++ b/src/tests/ofc24/4.device3_R1.json @@ -0,0 +1,188 @@ +{ + "devices": [ + { + "device_id": { + "device_uuid": { + "uuid": "R1" + } + }, + "device_type": "optical-roadm", + "device_drivers": [ + 11 + ], + "device_endpoints": [ + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "R1" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "2" + } + } + }, + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "R1" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "3" + } + } + }, + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "R1" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "12" + } + } + }, + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "R1" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "13" + } + } + } + ], + "device_operational_status": 1, + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "10.0.2.15" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "2025" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "username": "admin", + "password": "admin", + "force_running": false, + "hostkey_verify": false, + "look_for_keys": false, + "allow_agent": false, + "commit_per_rule": false, + "device_params": { + "name": "default" + }, + "manager_params": { + "timeout": 120 + }, + "endpoints": [ + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "2" + }, + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "3" + }, + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "12" + }, + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "13" + } + + ] + } + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc24/5.device4_R2.json b/src/tests/ofc24/5.device4_R2.json new file mode 100755 index 0000000000000000000000000000000000000000..9b1968d095c3e2c28c058b22f7295d7d1cbda380 --- /dev/null +++ b/src/tests/ofc24/5.device4_R2.json @@ -0,0 +1,189 @@ +{ + "devices": [ + { + "device_id": { + "device_uuid": { + "uuid": "R2" + } + }, + "device_type": "optical-roadm", + "device_drivers": [ + 11 + ], + "device_endpoints": [ + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "R2" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "4" + } + } + }, + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "R2" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "5" + } + } + }, + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "R2" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "14" + } + } + }, + { + "endpoint_id": { + "device_id": { + "device_uuid": { + "uuid": "R2" + } + }, + "topology_id": { + "context_id": { + "context_uuid": { + "uuid": "admin" + } + }, + "topology_uuid": { + "uuid": "admin" + } + }, + "endpoint_uuid": { + "uuid": "15" + } + } + } + + ], + "device_operational_status": 1, + "device_config": { + "config_rules": [ + { + "action": 1, + "custom": { + "resource_key": "_connect/address", + "resource_value": "10.0.2.15" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/port", + "resource_value": "2026" + } + }, + { + "action": 1, + "custom": { + "resource_key": "_connect/settings", + "resource_value": { + "username": "admin", + "password": "admin", + "force_running": false, + "hostkey_verify": false, + "look_for_keys": false, + "allow_agent": false, + "commit_per_rule": false, + "device_params": { + "name": "default" + }, + "manager_params": { + "timeout": 120 + }, + "endpoints": [ + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "4" + }, + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "5" + }, + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "14" + }, + { + "sample_types": [ + 101, + 102, + 201, + 202 + ], + "type": "optical", + "uuid": "15" + } + + ] + } + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/tests/ofc24/6.links.json b/src/tests/ofc24/6.links.json new file mode 100755 index 0000000000000000000000000000000000000000..eb2d004fc8e5484c5ae9eaafa6ec86efb49eb729 --- /dev/null +++ b/src/tests/ofc24/6.links.json @@ -0,0 +1,28 @@ +{ "links": [ + {"link_id": {"link_uuid": {"uuid": "T1->R1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "T1"}}, "endpoint_uuid": {"uuid": "1"}}, + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "12"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "R1->T1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2"}}, + {"device_id": {"device_uuid": {"uuid": "T1"}}, "endpoint_uuid": {"uuid": "1"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "R1->R2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "3"}}, + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "14"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "R2->R1"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "4"}}, + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "13"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "T2->R2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "T2"}}, "endpoint_uuid": {"uuid": "6"}}, + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "15"}} + ]}, + {"link_id": {"link_uuid": {"uuid": "R2->T2"}}, "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "5"}}, + {"device_id": {"device_uuid": {"uuid": "T2"}}, "endpoint_uuid": {"uuid": "6"}} + ]} +] + +} diff --git a/src/tests/ofc24/7.service-bidir.json b/src/tests/ofc24/7.service-bidir.json new file mode 100755 index 0000000000000000000000000000000000000000..05547a19d2d375d2a8202266b1a1a5ffcfe46eb6 --- /dev/null +++ b/src/tests/ofc24/7.service-bidir.json @@ -0,0 +1,22 @@ +{ + "services": [ + { + "service_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "service_uuid": {"uuid": "optical-connection"} + }, + "service_type": 6, + "service_status": {"service_status": 1}, + "service_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "T1"}}, "endpoint_uuid": {"uuid": "1"}}, + {"device_id": {"device_uuid": {"uuid": "T2"}}, "endpoint_uuid": {"uuid": "6"}} + ], + "service_constraints": [ + {"custom": {"constraint_type": "bandwidth[gbps]", "constraint_value": "100.0"}}, + {"custom": {"constraint_type": "bidirectionality", "constraint_value": "1"}}, + {"custom": {"constraint_type": "optical-band-width[GHz]", "constraint_value": "200"}} + ], + "service_config": {"config_rules": []} + } + ] +} diff --git a/src/tests/ofc24/7.service-unidir.json b/src/tests/ofc24/7.service-unidir.json new file mode 100755 index 0000000000000000000000000000000000000000..d9b4e88479c17aaf03f65d08b8d4b0ca22121e74 --- /dev/null +++ b/src/tests/ofc24/7.service-unidir.json @@ -0,0 +1,22 @@ +{ + "services": [ + { + "service_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "service_uuid": {"uuid": "optical-connection"} + }, + "service_type": 6, + "service_status": {"service_status": 1}, + "service_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "T1"}}, "endpoint_uuid": {"uuid": "1"}}, + {"device_id": {"device_uuid": {"uuid": "T2"}}, "endpoint_uuid": {"uuid": "6"}} + ], + "service_constraints": [ + {"custom": {"constraint_type": "bandwidth[gbps]", "constraint_value": "100.0"}}, + {"custom": {"constraint_type": "bidirectionality", "constraint_value": "0"}}, + {"custom": {"constraint_type": "optical-band-width[GHz]", "constraint_value": "200"}} + ], + "service_config": {"config_rules": []} + } + ] +} diff --git a/src/tests/ofc24/README.md b/src/tests/ofc24/README.md new file mode 100644 index 0000000000000000000000000000000000000000..93e95fc642273bf852c829d6fae14cddf8ccba96 --- /dev/null +++ b/src/tests/ofc24/README.md @@ -0,0 +1,21 @@ +# OFC'24 - Test scenario + +## Start Topology +Topology is composed of 2 transponders managed through OpenConfig and 2 Multi-granular ROAMDS +Strat the topology executing the following command: +```bash +sudo ./start_topo.sh +``` + +## Populate the TFS context and topology +Pushing the JSON files following the file indexes, i.e, 1, 2, 3, ... +The last JSON file with ID 7 is the service. +To check the service is onboarded successfully go into the TFS WebUI and check the `Service` tab. + +## Check configuration in devices +Check if the devices are configured properly. +To check that, run, for each device (X={1, 2, 3, 4}): +```bash +screen -r tX +``` +To release the terminal, press `Ctrl + A + D` diff --git a/src/tests/ofc24/plat_r1.xml b/src/tests/ofc24/plat_r1.xml new file mode 100755 index 0000000000000000000000000000000000000000..47e135c2e3752de21dbe2f17550026e9622f4de1 --- /dev/null +++ b/src/tests/ofc24/plat_r1.xml @@ -0,0 +1,120 @@ +<config xmlns="http://tail-f.com/ns/config/1.0"> + <components xmlns="http://openconfig.net/yang/platform"> + <component xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:operation="create"> + <name>2</name> + <config> + <name>2</name> + </config> + <properties> + <property> + <name>MG_ON_PORT_TYPE</name> + <config> + <name>MG_ON_PORT_TYPE</name> + <value>MG_ON_OPTICAL_PORT_WAVEBAND</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DIRECTION</name> + <config> + <name>MG_ON_PORT_DIRECTION</name> + <value>OUTPUT</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DEGREE</name> + <config> + <name>MG_ON_PORT_DEGREE</name> + <value>D1</value> + </config> + </property> + </properties> + </component> + <component xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:operation="create"> + <name>12</name> + <config> + <name>12</name> + </config> + <properties> + <property> + <name>MG_ON_PORT_TYPE</name> + <config> + <name>MG_ON_PORT_TYPE</name> + <value>MG_ON_OPTICAL_PORT_WAVEBAND</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DIRECTION</name> + <config> + <name>MG_ON_PORT_DIRECTION</name> + <value>INPUT</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DEGREE</name> + <config> + <name>MG_ON_PORT_DEGREE</name> + <value>D1</value> + </config> + </property> + </properties> + </component> + <component xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:operation="create"> + <name>3</name> + <config> + <name>3</name> + </config> + <properties> + <property> + <name>MG_ON_PORT_TYPE</name> + <config> + <name>MG_ON_PORT_TYPE</name> + <value>MG_ON_OPTICAL_PORT_WAVEBAND</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DIRECTION</name> + <config> + <name>MG_ON_PORT_DIRECTION</name> + <value>OUTPUT</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DEGREE</name> + <config> + <name>MG_ON_PORT_DEGREE</name> + <value>D2</value> + </config> + </property> + </properties> + </component> + <component xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:operation="create"> + <name>13</name> + <config> + <name>13</name> + </config> + <properties> + <property> + <name>MG_ON_PORT_TYPE</name> + <config> + <name>MG_ON_PORT_TYPE</name> + <value>MG_ON_OPTICAL_PORT_WAVEBAND</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DIRECTION</name> + <config> + <name>MG_ON_PORT_DIRECTION</name> + <value>INPUT</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DEGREE</name> + <config> + <name>MG_ON_PORT_DEGREE</name> + <value>D2</value> + </config> + </property> + </properties> + </component> + </components> +</config> \ No newline at end of file diff --git a/src/tests/ofc24/plat_r2.xml b/src/tests/ofc24/plat_r2.xml new file mode 100755 index 0000000000000000000000000000000000000000..dfaaf05ad7134a950ec11f411037b9a058b7d719 --- /dev/null +++ b/src/tests/ofc24/plat_r2.xml @@ -0,0 +1,120 @@ +<config xmlns="http://tail-f.com/ns/config/1.0"> + <components xmlns="http://openconfig.net/yang/platform"> + <component xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:operation="create"> + <name>4</name> + <config> + <name>4</name> + </config> + <properties> + <property> + <name>MG_ON_PORT_TYPE</name> + <config> + <name>MG_ON_PORT_TYPE</name> + <value>MG_ON_OPTICAL_PORT_WAVEBAND</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DIRECTION</name> + <config> + <name>MG_ON_PORT_DIRECTION</name> + <value>OUTPUT</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DEGREE</name> + <config> + <name>MG_ON_PORT_DEGREE</name> + <value>D1</value> + </config> + </property> + </properties> + </component> + <component xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:operation="create"> + <name>14</name> + <config> + <name>14</name> + </config> + <properties> + <property> + <name>MG_ON_PORT_TYPE</name> + <config> + <name>MG_ON_PORT_TYPE</name> + <value>MG_ON_OPTICAL_PORT_WAVEBAND</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DIRECTION</name> + <config> + <name>MG_ON_PORT_DIRECTION</name> + <value>INPUT</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DEGREE</name> + <config> + <name>MG_ON_PORT_DEGREE</name> + <value>D1</value> + </config> + </property> + </properties> + </component> + <component xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:operation="create"> + <name>5</name> + <config> + <name>5</name> + </config> + <properties> + <property> + <name>MG_ON_PORT_TYPE</name> + <config> + <name>MG_ON_PORT_TYPE</name> + <value>MG_ON_OPTICAL_PORT_WAVEBAND</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DIRECTION</name> + <config> + <name>MG_ON_PORT_DIRECTION</name> + <value>OUTPUT</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DEGREE</name> + <config> + <name>MG_ON_PORT_DEGREE</name> + <value>D2</value> + </config> + </property> + </properties> + </component> + <component xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:operation="create"> + <name>15</name> + <config> + <name>15</name> + </config> + <properties> + <property> + <name>MG_ON_PORT_TYPE</name> + <config> + <name>MG_ON_PORT_TYPE</name> + <value>MG_ON_OPTICAL_PORT_WAVEBAND</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DIRECTION</name> + <config> + <name>MG_ON_PORT_DIRECTION</name> + <value>INPUT</value> + </config> + </property> + <property> + <name>MG_ON_PORT_DEGREE</name> + <config> + <name>MG_ON_PORT_DEGREE</name> + <value>D2</value> + </config> + </property> + </properties> + </component> + </components> +</config> \ No newline at end of file diff --git a/src/tests/ofc24/startExtraNetConfigAgent.sh b/src/tests/ofc24/startExtraNetConfigAgent.sh new file mode 100755 index 0000000000000000000000000000000000000000..d9428585ef1040bb0440bf6db100b7f2bc71c970 --- /dev/null +++ b/src/tests/ofc24/startExtraNetConfigAgent.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +DOCKER_CONTAINER=$1 +DOCKER_PORT=$2 + +if [ -n "$DOCKER_CONTAINER" ] && [ -n "$DOCKER_PORT" ];then + sudo docker stop "$DOCKER_CONTAINER" -t 1 + sudo docker rm "$DOCKER_CONTAINER" + + echo "Creating TPs" + screen -dmS t1 -T xterm sh -c "docker run -p 10.0.2.15:"$DOCKER_PORT":2022 -v ~/tfs-ctrl/tempOC/files:/files --name $DOCKER_CONTAINER -it asgamb1/oc23bgp.img:latest" + sleep 2 + if [ "$( docker container inspect -f '{{.State.Running}}' "$DOCKER_CONTAINER")" = "true" ]; then + docker exec "$DOCKER_CONTAINER" cp /files/demoECOC21_4.xml demoECOC21.xml + docker exec "$DOCKER_CONTAINER" /confd/examples.confd/OC23/startNetconfAgent.sh + else + echo "your container is not running yet" + fi +else + echo "Please define the docker container name and port" +fi diff --git a/src/tests/ofc24/start_topo.sh b/src/tests/ofc24/start_topo.sh new file mode 100755 index 0000000000000000000000000000000000000000..c924064763c14e4da45344cd21f4d9c81c9640a9 --- /dev/null +++ b/src/tests/ofc24/start_topo.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +IMAGE_NAME="asgamb1/oc23bgp.img:latest" +DOCKER_CONTAINER=$1 +DOCKER_PORT="2022" + +sudo docker stop na1 -t 1 +sudo docker stop na2 -t 1 +sudo docker stop na3 -t 1 +sudo docker stop na4 -t 1 + +sudo docker rm na2 +sudo docker rm na1 +sudo docker rm na3 +sudo docker rm na4 + +echo "Creating Transponder Agents" + +# if ! docker image inspect "$IMAGE_NAME" >/dev/null 2>&1 ; then +# echo "asgamb1/oc23bgp.img:latest not existed ! " +# screen -dmS t3 -T xterm sh -c "docker run -p 10.0.2.15:2025:2022 -v ~/tempOC/files:/files --name na -it $IMAGE_NAME bash" +# echo 'start downloading asgamb1/oc23bgp.img:latest , it may take few minutes ! .... ' +# while [ "$(docker image inspect asgamb1/oc23bgp.img:latest 2>/dev/null)" == "[]" ]; do +# sleep 1 +# done + +#fi + + + +screen -dmS t1 -T xterm sh -c "docker run -p 127.0.0.1:2023:2022 -v ~/tempOC/files:/files --name $DOCKER_CONTAINER -it asgamb1/oc23bgp.img:latest bash" +sleep 2 +if [ "$( docker container inspect -f '{{.State.Running}}' "$DOCKER_CONTAINER")" = "true" ]; then + docker exec "$DOCKER_CONTAINER" cp /files/demoECOC21_4.xml demoECOC21.xml + docker exec "$DOCKER_CONTAINER" /confd/examples.confd/OC23/startNetconfAgent.sh +else + echo "your container is not running yet" +fi + +echo " It may take a while , Hang on ..." +source "./startExtraNetConfigAgent.sh" "na1" "2023" +sleep 3 + +source "./startExtraNetConfigAgent.sh" "na2" "2024" +sleep 3 + +bash -c "cp /tempOC/files/plat_r1.xml /confd/examples.confd/OC23/init_openconfig-platform.xml; ./startNetconfAgent.sh" +bash -c "cp /tempOC/files/plat_r2.xml /confd/examples.confd/OC23/init_openconfig-platform.xml; ./startNetconfAgent.sh" +screen -dmS t3 -T xterm sh -c 'docker run -p 10.0.2.15:2025:2022 -v ~/tfs-ctrl/tempOC/files:/files --name na3 -it asgamb1/flexscale-node.img:latest ./startNetconfAgent.sh' +screen -dmS t4 -T xterm sh -c 'docker run -p 10.0.2.15:2026:2022 -v ~/tfs-ctrl/tempOC/files:/files --name na4 -it asgamb1/flexscale-node.img:latest ./startNetconfAgent.sh' diff --git a/src/tests/ofc24/t1.xml b/src/tests/ofc24/t1.xml new file mode 100755 index 0000000000000000000000000000000000000000..712615df8dd821ff8e79df9785d6d29324a25b7d --- /dev/null +++ b/src/tests/ofc24/t1.xml @@ -0,0 +1,298 @@ +<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <components xmlns="http://openconfig.net/yang/platform"> + <component> + <name>device</name> + <config> + <name>device</name> + </config> + <state> + <name>MellanoxSwitch</name> + <mfg-name>SSSA-CNIT</mfg-name> + <hardware-version>1.0.0</hardware-version> + <firmware-version>1.0.0</firmware-version> + <software-version>1.0.0</software-version> + <serial-no>610610</serial-no> + <type xmlns:typex="http://openconfig.net/yang/platform-types">typex:OPERATING_SYSTEM</type> + </state> + </component> + <component> + <name>channel-1</name> + <config> + <name>channel-1</name> + </config> + <state> + <name>channel-1</name> + <type xmlns:typex="http://openconfig.net/yang/transport-types">typex:OPTICAL_CHANNEL</type> + </state> + <optical-channel xmlns="http://openconfig.net/yang/terminal-device"> + <config> + <frequency>191600000</frequency> + <target-output-power>100</target-output-power> + <operational-mode>0</operational-mode> + <line-port>transceiver-1</line-port> + </config> + <state> + <frequency>191600000</frequency> + <target-output-power>0</target-output-power> + <operational-mode>0</operational-mode> + <line-port>transceiver-1</line-port> + <group-id>1</group-id> + <output-power> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + <interval>0</interval> + </output-power> + <input-power> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + <interval>0</interval> + </input-power> + <laser-bias-current> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + <interval>0</interval> + </laser-bias-current> + <chromatic-dispersion> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + </chromatic-dispersion> + <polarization-mode-dispersion> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + </polarization-mode-dispersion> + <second-order-polarization-mode-dispersion> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + </second-order-polarization-mode-dispersion> + <polarization-dependent-loss> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + <interval>0</interval> + </polarization-dependent-loss> + </state> + </optical-channel> + </component> + <component> + <name>transceiver-1</name> + <config> + <name>transceiver-1</name> + </config> + <state> + <name>transceiver-1</name> + <type xmlns:typex="http://openconfig.net/yang/platform-types">typex:TRANSCEIVER</type> + </state> + <transceiver xmlns="http://openconfig.net/yang/platform/transceiver"> + <config> + <enabled>true</enabled> + <form-factor-preconf xmlns:typex="http://openconfig.net/yang/transport-types">typex:QSFP56_DD_TYPE1</form-factor-preconf> + <ethernet-pmd-preconf xmlns:typex="http://openconfig.net/yang/transport-types">typex:ETH_400GBASE_ZR</ethernet-pmd-preconf> + <fec-mode xmlns:typex="http://openconfig.net/yang/platform-types">typex:FEC_AUTO</fec-mode> + <module-functional-type xmlns:typex="http://openconfig.net/yang/transport-types">typex:TYPE_DIGITAL_COHERENT_OPTIC</module-functional-type> + </config> + <state> + <enabled>true</enabled> + <form-factor-preconf xmlns:typex="http://openconfig.net/yang/transport-types">typex:QSFP56_DD_TYPE1</form-factor-preconf> + <ethernet-pmd-preconf xmlns:typex="http://openconfig.net/yang/transport-types">typex:ETH_400GBASE_ZR</ethernet-pmd-preconf> + <fec-mode xmlns:typex="http://openconfig.net/yang/platform-types">typex:FEC_AUTO</fec-mode> + <module-functional-type xmlns:typex="http://openconfig.net/yang/transport-types">typex:TYPE_DIGITAL_COHERENT_OPTIC</module-functional-type> + <vendor>Cisco</vendor> + <vendor-part>400zr-QSFP-DD</vendor-part> + <vendor-rev>01</vendor-rev> + <serial-no>1567321</serial-no> + </state> + <physical-channels> + <channel> + <index>1</index> + <config> + <index>1</index> + <associated-optical-channel>channel-1</associated-optical-channel> + </config> + <!--state> + <index>1</index> + <associated-optical-channel>channel-4</associated-optical-channel> + </state--> + </channel> + </physical-channels> + </transceiver> + </component> + <component> + <name>port-1</name> + <config> + <name>port-1</name> + </config> + <state> + <name>port-1</name> + <type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type> + </state> + <subcomponents> + <subcomponent> + <name>channel-1</name> + <config> + <name>channel-1</name> + </config> + <state> + <name>channel-1</name> + </state> + </subcomponent> + </subcomponents> + <properties> + <property> + <name>onos-index</name> + <config> + <name>onos-index</name> + <value>4</value> + </config> + <state> + <name>onos-index</name> + <value>4</value> + </state> + </property> + <property> + <name>odtn-port-type</name> + <config> + <name>odtn-port-type</name> + <value>line</value> + </config> + <state> + <name>odtn-port-type</name> + <value>line</value> + </state> + </property> + </properties> + </component> + + + </components> + <terminal-device xmlns="http://openconfig.net/yang/terminal-device"> + <logical-channels> + <!--Description: Optical logical link--> + <channel> + + <!--Description: Line (OTN) Port--> + <index>1</index> + <config> + <index>1</index> + <description>Logical channel 1</description> + <admin-state>DISABLED</admin-state> + <logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_OTN</logical-channel-type> + <loopback-mode>NONE</loopback-mode> + </config> + <state> + <index>1</index> + <description>Logical channel 1</description> + <admin-state>DISABLED</admin-state> + <logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_OTN</logical-channel-type> + <loopback-mode>NONE</loopback-mode> + <link-state>UP</link-state> + </state> + <ingress> + <config> + <transceiver>transceiver-1</transceiver> + </config> + <state> + <transceiver>transceiver-1</transceiver> + </state> + </ingress> + <otn> + <config> + <tti-msg-expected>test1</tti-msg-expected> + <tti-msg-transmit>test1</tti-msg-transmit> + </config> + <state> + <tti-msg-expected>test1</tti-msg-expected> + <tti-msg-transmit>test1</tti-msg-transmit> + <tti-msg-auto>0</tti-msg-auto> + <tti-msg-recv>0</tti-msg-recv> + <rdi-msg>0</rdi-msg> + <errored-seconds>0</errored-seconds> + <severely-errored-seconds>0</severely-errored-seconds> + <unavailable-seconds>0</unavailable-seconds> + <code-violations>0</code-violations> + <fec-uncorrectable-words>0</fec-uncorrectable-words> + <fec-corrected-bytes>0</fec-corrected-bytes> + <fec-corrected-bits>0</fec-corrected-bits> + <background-block-errors>0</background-block-errors> + <pre-fec-ber> + <instant>0.0</instant> + <avg>0.0</avg> + <min>0.0</min> + <max>0.0</max> + </pre-fec-ber> + <post-fec-ber> + <instant>0.0</instant> + <avg>0.0</avg> + <min>0.0</min> + <max>0.0</max> + </post-fec-ber> + <q-value> + <instant>0.0</instant> + <avg>0.0</avg> + <min>0.0</min> + <max>0.0</max> + <interval>0</interval> + </q-value> + <esnr> + <instant>0.0</instant> + <avg>0.0</avg> + <min>0.0</min> + <max>0.0</max> + <interval>0</interval> + </esnr> + </state> + </otn> + <logical-channel-assignments> + <assignment> + <index>1</index> + <config> + <index>1</index> + <description>Optical channel assigned 100</description> + <allocation>100</allocation> + <assignment-type>OPTICAL_CHANNEL</assignment-type> + <optical-channel>channel-1</optical-channel> + </config> + <state> + <index>1</index> + <description>Optical channel assigned 100</description> + <allocation>100</allocation> + <assignment-type>OPTICAL_CHANNEL</assignment-type> + <optical-channel>channel-1</optical-channel> + </state> + </assignment> + </logical-channel-assignments> + </channel> + </logical-channels> + <operational-modes> + <mode> + <mode-id>1</mode-id> + <state> + <mode-id>1</mode-id> + <description>FEC1</description> + <vendor-id>Ericsson</vendor-id> + </state> + </mode> + <mode> + <mode-id>2</mode-id> + <state> + <mode-id>2</mode-id> + <description>FEC2</description> + <vendor-id>Ericsson</vendor-id> + </state> + </mode> + </operational-modes> + </terminal-device> +</config> + diff --git a/src/tests/ofc24/t2.xml b/src/tests/ofc24/t2.xml new file mode 100755 index 0000000000000000000000000000000000000000..3a35e7e87eac7b14cd2bfa693560cc653f3719b9 --- /dev/null +++ b/src/tests/ofc24/t2.xml @@ -0,0 +1,298 @@ +<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0"> + <components xmlns="http://openconfig.net/yang/platform"> + <component> + <name>device</name> + <config> + <name>device</name> + </config> + <state> + <name>MellanoxSwitch</name> + <mfg-name>SSSA-CNIT</mfg-name> + <hardware-version>1.0.0</hardware-version> + <firmware-version>1.0.0</firmware-version> + <software-version>1.0.0</software-version> + <serial-no>610610</serial-no> + <type xmlns:typex="http://openconfig.net/yang/platform-types">typex:OPERATING_SYSTEM</type> + </state> + </component> + <component> + <name>channel-6</name> + <config> + <name>channel-6</name> + </config> + <state> + <name>channel-6</name> + <type xmlns:typex="http://openconfig.net/yang/transport-types">typex:OPTICAL_CHANNEL</type> + </state> + <optical-channel xmlns="http://openconfig.net/yang/terminal-device"> + <config> + <frequency>191600000</frequency> + <target-output-power>100</target-output-power> + <operational-mode>0</operational-mode> + <line-port>transceiver-6</line-port> + </config> + <state> + <frequency>191600000</frequency> + <target-output-power>0</target-output-power> + <operational-mode>0</operational-mode> + <line-port>transceiver-6</line-port> + <group-id>1</group-id> + <output-power> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + <interval>0</interval> + </output-power> + <input-power> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + <interval>0</interval> + </input-power> + <laser-bias-current> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + <interval>0</interval> + </laser-bias-current> + <chromatic-dispersion> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + </chromatic-dispersion> + <polarization-mode-dispersion> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + </polarization-mode-dispersion> + <second-order-polarization-mode-dispersion> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + </second-order-polarization-mode-dispersion> + <polarization-dependent-loss> + <instant>0</instant> + <avg>0</avg> + <min>0</min> + <max>0</max> + <interval>0</interval> + </polarization-dependent-loss> + </state> + </optical-channel> + </component> + <component> + <name>transceiver-6</name> + <config> + <name>transceiver-6</name> + </config> + <state> + <name>transceiver-6</name> + <type xmlns:typex="http://openconfig.net/yang/platform-types">typex:TRANSCEIVER</type> + </state> + <transceiver xmlns="http://openconfig.net/yang/platform/transceiver"> + <config> + <enabled>true</enabled> + <form-factor-preconf xmlns:typex="http://openconfig.net/yang/transport-types">typex:QSFP56_DD_TYPE1</form-factor-preconf> + <ethernet-pmd-preconf xmlns:typex="http://openconfig.net/yang/transport-types">typex:ETH_400GBASE_ZR</ethernet-pmd-preconf> + <fec-mode xmlns:typex="http://openconfig.net/yang/platform-types">typex:FEC_AUTO</fec-mode> + <module-functional-type xmlns:typex="http://openconfig.net/yang/transport-types">typex:TYPE_DIGITAL_COHERENT_OPTIC</module-functional-type> + </config> + <state> + <enabled>true</enabled> + <form-factor-preconf xmlns:typex="http://openconfig.net/yang/transport-types">typex:QSFP56_DD_TYPE1</form-factor-preconf> + <ethernet-pmd-preconf xmlns:typex="http://openconfig.net/yang/transport-types">typex:ETH_400GBASE_ZR</ethernet-pmd-preconf> + <fec-mode xmlns:typex="http://openconfig.net/yang/platform-types">typex:FEC_AUTO</fec-mode> + <module-functional-type xmlns:typex="http://openconfig.net/yang/transport-types">typex:TYPE_DIGITAL_COHERENT_OPTIC</module-functional-type> + <vendor>Cisco</vendor> + <vendor-part>400zr-QSFP-DD</vendor-part> + <vendor-rev>01</vendor-rev> + <serial-no>1567321</serial-no> + </state> + <physical-channels> + <channel> + <index>1</index> + <config> + <index>1</index> + <associated-optical-channel>channel-6</associated-optical-channel> + </config> + <!--state> + <index>1</index> + <associated-optical-channel>channel-4</associated-optical-channel> + </state--> + </channel> + </physical-channels> + </transceiver> + </component> + <component> + <name>port-6</name> + <config> + <name>port-6</name> + </config> + <state> + <name>port-6</name> + <type xmlns:typex="http://openconfig.net/yang/platform-types">typex:PORT</type> + </state> + <subcomponents> + <subcomponent> + <name>channel-6</name> + <config> + <name>channel-6</name> + </config> + <state> + <name>channel-6</name> + </state> + </subcomponent> + </subcomponents> + <properties> + <property> + <name>onos-index</name> + <config> + <name>onos-index</name> + <value>4</value> + </config> + <state> + <name>onos-index</name> + <value>4</value> + </state> + </property> + <property> + <name>odtn-port-type</name> + <config> + <name>odtn-port-type</name> + <value>line</value> + </config> + <state> + <name>odtn-port-type</name> + <value>line</value> + </state> + </property> + </properties> + </component> + + + </components> + <terminal-device xmlns="http://openconfig.net/yang/terminal-device"> + <logical-channels> + <!--Description: Optical logical link--> + <channel> + + <!--Description: Line (OTN) Port--> + <index>4</index> + <config> + <index>4</index> + <description>Logical channel 4</description> + <admin-state>DISABLED</admin-state> + <logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_OTN</logical-channel-type> + <loopback-mode>NONE</loopback-mode> + </config> + <state> + <index>4</index> + <description>Logical channel 4</description> + <admin-state>DISABLED</admin-state> + <logical-channel-type xmlns:type="http://openconfig.net/yang/transport-types">type:PROT_OTN</logical-channel-type> + <loopback-mode>NONE</loopback-mode> + <link-state>UP</link-state> + </state> + <ingress> + <config> + <transceiver>transceiver-6</transceiver> + </config> + <state> + <transceiver>transceiver-6</transceiver> + </state> + </ingress> + <otn> + <config> + <tti-msg-expected>test1</tti-msg-expected> + <tti-msg-transmit>test1</tti-msg-transmit> + </config> + <state> + <tti-msg-expected>test1</tti-msg-expected> + <tti-msg-transmit>test1</tti-msg-transmit> + <tti-msg-auto>0</tti-msg-auto> + <tti-msg-recv>0</tti-msg-recv> + <rdi-msg>0</rdi-msg> + <errored-seconds>0</errored-seconds> + <severely-errored-seconds>0</severely-errored-seconds> + <unavailable-seconds>0</unavailable-seconds> + <code-violations>0</code-violations> + <fec-uncorrectable-words>0</fec-uncorrectable-words> + <fec-corrected-bytes>0</fec-corrected-bytes> + <fec-corrected-bits>0</fec-corrected-bits> + <background-block-errors>0</background-block-errors> + <pre-fec-ber> + <instant>0.0</instant> + <avg>0.0</avg> + <min>0.0</min> + <max>0.0</max> + </pre-fec-ber> + <post-fec-ber> + <instant>0.0</instant> + <avg>0.0</avg> + <min>0.0</min> + <max>0.0</max> + </post-fec-ber> + <q-value> + <instant>0.0</instant> + <avg>0.0</avg> + <min>0.0</min> + <max>0.0</max> + <interval>0</interval> + </q-value> + <esnr> + <instant>0.0</instant> + <avg>0.0</avg> + <min>0.0</min> + <max>0.0</max> + <interval>0</interval> + </esnr> + </state> + </otn> + <logical-channel-assignments> + <assignment> + <index>1</index> + <config> + <index>1</index> + <description>Optical channel assigned 100</description> + <allocation>100</allocation> + <assignment-type>OPTICAL_CHANNEL</assignment-type> + <optical-channel>channel-6</optical-channel> + </config> + <state> + <index>1</index> + <description>Optical channel assigned 100</description> + <allocation>100</allocation> + <assignment-type>OPTICAL_CHANNEL</assignment-type> + <optical-channel>channel-6</optical-channel> + </state> + </assignment> + </logical-channel-assignments> + </channel> + </logical-channels> + <operational-modes> + <mode> + <mode-id>1</mode-id> + <state> + <mode-id>1</mode-id> + <description>FEC1</description> + <vendor-id>Ericsson</vendor-id> + </state> + </mode> + <mode> + <mode-id>2</mode-id> + <state> + <mode-id>2</mode-id> + <description>FEC2</description> + <vendor-id>Ericsson</vendor-id> + </state> + </mode> + </operational-modes> + </terminal-device> +</config> +