Loading deploy/all.sh +3 −0 Viewed Changes for deploy/all.sh: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -69,6 +69,9 @@ export TFS_COMPONENTS=${TFS_COMPONENTS:-"context device pathcomp service slice n # Uncomment to activate E2E Orchestrator #export TFS_COMPONENTS="${TFS_COMPONENTS} e2e_orchestrator" # Uncomment to activate Pluggables Component #export TFS_COMPONENTS="${TFS_COMPONENTS} pluggables" # If not already set, set the tag you want to use for your images. export TFS_IMAGE_TAG=${TFS_IMAGE_TAG:-"dev"} Loading proto/pluggables.proto 0 → 100644 +139 −0 Viewed Changes for proto/pluggables.proto: 139 added lines, 0 removed lines. Original line number Diff line number Diff line // Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package pluggables; import "context.proto"; service PluggablesService { rpc CreatePluggable (CreatePluggableRequest) returns (Pluggable) {} rpc ListPluggables (ListPluggablesRequest) returns (ListPluggablesResponse) {} rpc GetPluggable (GetPluggableRequest) returns (Pluggable) {} rpc DeletePluggable (DeletePluggableRequest) returns (context.Empty) {} rpc ConfigurePluggable (ConfigurePluggableRequest) returns (Pluggable) {} } message PluggableId { context.DeviceId device = 1; int32 pluggable_index = 2; // physical slot number in the device } message DigitalSubcarrierGroupId { PluggableId pluggable = 1; int32 group_index = 2; // Group id within the pluggable } message DigitalSubcarrierId { DigitalSubcarrierGroupId group = 1; int32 subcarrier_index = 2; // Subcarrier index within the group } message DigitalSubcarrierConfig { DigitalSubcarrierId id = 1; bool active = 2; double target_output_power_dbm = 3; double center_frequency_hz = 10; double symbol_rate_baud = 11; } message DigitalSubcarrierState { DigitalSubcarrierId id = 1; bool active = 2; double measured_output_power_dbm = 3; double osnr_db = 4; context.Timestamp updated_at = 10; } message DigitalSubcarrierGroupConfig { DigitalSubcarrierGroupId id = 1; int32 group_size = 2; // expected number of DSCs double group_capacity_gbps = 3; // from YANG group capacity in Gbps double subcarrier_spacing_mhz = 4; // from YANG digital-subcarrier-spacing repeated DigitalSubcarrierConfig subcarriers = 10; } message DigitalSubcarrierGroupState { DigitalSubcarrierGroupId id = 1; int32 count = 2; // available DSCs double group_capacity_gbps = 3; double subcarrier_spacing_mhz = 4; repeated DigitalSubcarrierState subcarriers = 10; context.Timestamp updated_at = 20; } message PluggableConfig { PluggableId id = 1; double target_output_power_dbm = 2; // target output power for the pluggable double center_frequency_mhz = 3; // center frequency in MHz int32 operational_mode = 4; // e.g., 0=off and 1=on int32 line_port = 5; // line port number repeated DigitalSubcarrierGroupConfig dsc_groups = 10; } message PluggableState { PluggableId id = 1; repeated DigitalSubcarrierGroupState dsc_groups = 10; context.Timestamp updated_at = 20; } message Pluggable { PluggableId id = 1; PluggableConfig config = 2; PluggableState state = 3; } // ----------------------------------------------------------------------------- // RPC I/O Messages // ----------------------------------------------------------------------------- message CreatePluggableRequest { context.DeviceId device = 1; int32 preferred_pluggable_index = 2; // -1 for auto (physical slot number in the device(router)) PluggableConfig initial_config = 10; } message ListPluggablesRequest { context.DeviceId device = 1; View view_level = 2; } message ListPluggablesResponse { repeated Pluggable pluggables = 1; } message GetPluggableRequest { PluggableId id = 1; View view_level = 2; } message DeletePluggableRequest { PluggableId id = 1; } message ConfigurePluggableRequest { PluggableConfig config = 1; View view_level = 2; int32 apply_timeout_seconds = 10; // Not Implemented yet (for timeout) } // to control the level of detail in responses enum View { VIEW_UNSPECIFIED = 0; VIEW_CONFIG = 1; VIEW_STATE = 2; VIEW_FULL = 3; } scripts/run_tests_locally-service-pluggable.sh 0 → 100755 +23 −0 Viewed Changes for scripts/run_tests_locally-service-pluggable.sh: 23 added lines, 0 removed lines. Original line number Diff line number Diff line #!/bin/bash # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. PROJECTDIR=`pwd` cd $PROJECTDIR/src RCFILE=$PROJECTDIR/coverage/.coveragerc python3 -m pytest --log-level=info --log-cli-level=info --verbose \ pluggables/tests/test_Pluggables.py echo "Bye!" src/common/method_wrappers/ServiceExceptions.py +5 −5 Viewed Changes for src/common/method_wrappers/ServiceExceptions.py: 5 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -33,21 +33,21 @@ class NotFoundException(ServiceException): class AlreadyExistsException(ServiceException): def __init__( self, object_name : str, object_uuid: str, extra_details : Union[str, Iterable[str]] = None self, object_name : str, object_uuid: str, extra_details : Union[str, Iterable[str]] = [] ) -> None: details = '{:s}({:s}) already exists'.format(str(object_name), str(object_uuid)) super().__init__(grpc.StatusCode.ALREADY_EXISTS, details, extra_details=extra_details) class InvalidArgumentException(ServiceException): def __init__( self, argument_name : str, argument_value: str, extra_details : Union[str, Iterable[str]] = None self, argument_name : str, argument_value: str, extra_details : Union[str, Iterable[str]] = [] ) -> None: details = '{:s}({:s}) is invalid'.format(str(argument_name), str(argument_value)) super().__init__(grpc.StatusCode.INVALID_ARGUMENT, details, extra_details=extra_details) class InvalidArgumentsException(ServiceException): def __init__( self, arguments : List[Tuple[str, str]], extra_details : Union[str, Iterable[str]] = None self, arguments : List[Tuple[str, str]], extra_details : Union[str, Iterable[str]] = [] ) -> None: str_arguments = ', '.join(['{:s}({:s})'.format(name, value) for name,value in arguments]) details = 'Arguments {:s} are invalid'.format(str_arguments) Loading @@ -55,14 +55,14 @@ class InvalidArgumentsException(ServiceException): class OperationFailedException(ServiceException): def __init__( self, operation : str, extra_details : Union[str, Iterable[str]] = None self, operation : str, extra_details : Union[str, Iterable[str]] = [] ) -> None: details = 'Operation({:s}) failed'.format(str(operation)) super().__init__(grpc.StatusCode.INTERNAL, details, extra_details=extra_details) class NotImplementedException(ServiceException): def __init__( self, operation : str, extra_details : Union[str, Iterable[str]] = None self, operation : str, extra_details : Union[str, Iterable[str]] = [] ) -> None: details = 'Operation({:s}) not implemented'.format(str(operation)) super().__init__(grpc.StatusCode.UNIMPLEMENTED, details, extra_details=extra_details) src/common/Constants.py +2 −0 Viewed Changes for src/common/Constants.py: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ class ServiceNameEnum(Enum): ANALYTICSBACKEND = 'analytics-backend' QOSPROFILE = 'qos-profile' OSMCLIENT = 'osm-client' PLUGGABLES = 'dscm-pluggable' # Used for test and debugging only DLT_GATEWAY = 'dltgateway' Loading Loading @@ -117,6 +118,7 @@ DEFAULT_SERVICE_GRPC_PORTS = { ServiceNameEnum.ANALYTICSBACKEND .value : 30090, ServiceNameEnum.AUTOMATION .value : 30200, ServiceNameEnum.OSMCLIENT .value : 30210, ServiceNameEnum.PLUGGABLES .value : 30220, # Used for test and debugging only ServiceNameEnum.DLT_GATEWAY .value : 50051, Loading Loading
deploy/all.sh +3 −0 Viewed Changes for deploy/all.sh: 3 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -69,6 +69,9 @@ export TFS_COMPONENTS=${TFS_COMPONENTS:-"context device pathcomp service slice n # Uncomment to activate E2E Orchestrator #export TFS_COMPONENTS="${TFS_COMPONENTS} e2e_orchestrator" # Uncomment to activate Pluggables Component #export TFS_COMPONENTS="${TFS_COMPONENTS} pluggables" # If not already set, set the tag you want to use for your images. export TFS_IMAGE_TAG=${TFS_IMAGE_TAG:-"dev"} Loading
proto/pluggables.proto 0 → 100644 +139 −0 Viewed Changes for proto/pluggables.proto: 139 added lines, 0 removed lines. Original line number Diff line number Diff line // Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. syntax = "proto3"; package pluggables; import "context.proto"; service PluggablesService { rpc CreatePluggable (CreatePluggableRequest) returns (Pluggable) {} rpc ListPluggables (ListPluggablesRequest) returns (ListPluggablesResponse) {} rpc GetPluggable (GetPluggableRequest) returns (Pluggable) {} rpc DeletePluggable (DeletePluggableRequest) returns (context.Empty) {} rpc ConfigurePluggable (ConfigurePluggableRequest) returns (Pluggable) {} } message PluggableId { context.DeviceId device = 1; int32 pluggable_index = 2; // physical slot number in the device } message DigitalSubcarrierGroupId { PluggableId pluggable = 1; int32 group_index = 2; // Group id within the pluggable } message DigitalSubcarrierId { DigitalSubcarrierGroupId group = 1; int32 subcarrier_index = 2; // Subcarrier index within the group } message DigitalSubcarrierConfig { DigitalSubcarrierId id = 1; bool active = 2; double target_output_power_dbm = 3; double center_frequency_hz = 10; double symbol_rate_baud = 11; } message DigitalSubcarrierState { DigitalSubcarrierId id = 1; bool active = 2; double measured_output_power_dbm = 3; double osnr_db = 4; context.Timestamp updated_at = 10; } message DigitalSubcarrierGroupConfig { DigitalSubcarrierGroupId id = 1; int32 group_size = 2; // expected number of DSCs double group_capacity_gbps = 3; // from YANG group capacity in Gbps double subcarrier_spacing_mhz = 4; // from YANG digital-subcarrier-spacing repeated DigitalSubcarrierConfig subcarriers = 10; } message DigitalSubcarrierGroupState { DigitalSubcarrierGroupId id = 1; int32 count = 2; // available DSCs double group_capacity_gbps = 3; double subcarrier_spacing_mhz = 4; repeated DigitalSubcarrierState subcarriers = 10; context.Timestamp updated_at = 20; } message PluggableConfig { PluggableId id = 1; double target_output_power_dbm = 2; // target output power for the pluggable double center_frequency_mhz = 3; // center frequency in MHz int32 operational_mode = 4; // e.g., 0=off and 1=on int32 line_port = 5; // line port number repeated DigitalSubcarrierGroupConfig dsc_groups = 10; } message PluggableState { PluggableId id = 1; repeated DigitalSubcarrierGroupState dsc_groups = 10; context.Timestamp updated_at = 20; } message Pluggable { PluggableId id = 1; PluggableConfig config = 2; PluggableState state = 3; } // ----------------------------------------------------------------------------- // RPC I/O Messages // ----------------------------------------------------------------------------- message CreatePluggableRequest { context.DeviceId device = 1; int32 preferred_pluggable_index = 2; // -1 for auto (physical slot number in the device(router)) PluggableConfig initial_config = 10; } message ListPluggablesRequest { context.DeviceId device = 1; View view_level = 2; } message ListPluggablesResponse { repeated Pluggable pluggables = 1; } message GetPluggableRequest { PluggableId id = 1; View view_level = 2; } message DeletePluggableRequest { PluggableId id = 1; } message ConfigurePluggableRequest { PluggableConfig config = 1; View view_level = 2; int32 apply_timeout_seconds = 10; // Not Implemented yet (for timeout) } // to control the level of detail in responses enum View { VIEW_UNSPECIFIED = 0; VIEW_CONFIG = 1; VIEW_STATE = 2; VIEW_FULL = 3; }
scripts/run_tests_locally-service-pluggable.sh 0 → 100755 +23 −0 Viewed Changes for scripts/run_tests_locally-service-pluggable.sh: 23 added lines, 0 removed lines. Original line number Diff line number Diff line #!/bin/bash # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. PROJECTDIR=`pwd` cd $PROJECTDIR/src RCFILE=$PROJECTDIR/coverage/.coveragerc python3 -m pytest --log-level=info --log-cli-level=info --verbose \ pluggables/tests/test_Pluggables.py echo "Bye!"
src/common/method_wrappers/ServiceExceptions.py +5 −5 Viewed Changes for src/common/method_wrappers/ServiceExceptions.py: 5 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -33,21 +33,21 @@ class NotFoundException(ServiceException): class AlreadyExistsException(ServiceException): def __init__( self, object_name : str, object_uuid: str, extra_details : Union[str, Iterable[str]] = None self, object_name : str, object_uuid: str, extra_details : Union[str, Iterable[str]] = [] ) -> None: details = '{:s}({:s}) already exists'.format(str(object_name), str(object_uuid)) super().__init__(grpc.StatusCode.ALREADY_EXISTS, details, extra_details=extra_details) class InvalidArgumentException(ServiceException): def __init__( self, argument_name : str, argument_value: str, extra_details : Union[str, Iterable[str]] = None self, argument_name : str, argument_value: str, extra_details : Union[str, Iterable[str]] = [] ) -> None: details = '{:s}({:s}) is invalid'.format(str(argument_name), str(argument_value)) super().__init__(grpc.StatusCode.INVALID_ARGUMENT, details, extra_details=extra_details) class InvalidArgumentsException(ServiceException): def __init__( self, arguments : List[Tuple[str, str]], extra_details : Union[str, Iterable[str]] = None self, arguments : List[Tuple[str, str]], extra_details : Union[str, Iterable[str]] = [] ) -> None: str_arguments = ', '.join(['{:s}({:s})'.format(name, value) for name,value in arguments]) details = 'Arguments {:s} are invalid'.format(str_arguments) Loading @@ -55,14 +55,14 @@ class InvalidArgumentsException(ServiceException): class OperationFailedException(ServiceException): def __init__( self, operation : str, extra_details : Union[str, Iterable[str]] = None self, operation : str, extra_details : Union[str, Iterable[str]] = [] ) -> None: details = 'Operation({:s}) failed'.format(str(operation)) super().__init__(grpc.StatusCode.INTERNAL, details, extra_details=extra_details) class NotImplementedException(ServiceException): def __init__( self, operation : str, extra_details : Union[str, Iterable[str]] = None self, operation : str, extra_details : Union[str, Iterable[str]] = [] ) -> None: details = 'Operation({:s}) not implemented'.format(str(operation)) super().__init__(grpc.StatusCode.UNIMPLEMENTED, details, extra_details=extra_details)
src/common/Constants.py +2 −0 Viewed Changes for src/common/Constants.py: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -75,6 +75,7 @@ class ServiceNameEnum(Enum): ANALYTICSBACKEND = 'analytics-backend' QOSPROFILE = 'qos-profile' OSMCLIENT = 'osm-client' PLUGGABLES = 'dscm-pluggable' # Used for test and debugging only DLT_GATEWAY = 'dltgateway' Loading Loading @@ -117,6 +118,7 @@ DEFAULT_SERVICE_GRPC_PORTS = { ServiceNameEnum.ANALYTICSBACKEND .value : 30090, ServiceNameEnum.AUTOMATION .value : 30200, ServiceNameEnum.OSMCLIENT .value : 30210, ServiceNameEnum.PLUGGABLES .value : 30220, # Used for test and debugging only ServiceNameEnum.DLT_GATEWAY .value : 50051, Loading