Loading proto/logical_resources.proto +1 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ service LogicalResources { rpc GetAvailableResources (Empty) returns (ResourceList) {} rpc GetAvailableResources (Empty) returns (ResourceList) {} rpc ReserveResource (ResourceReservation) returns (Success) {} rpc ReserveResource (ResourceReservation) returns (Success) {} rpc ReleaseResource (ResourceReservation) returns (Success) {} rpc ReleaseResource (ResourceReservation) returns (Success) {} rpc DeleteResource (ResourceReservation) returns (Success) {} rpc GetReservedResources (Empty) returns (ResourceList) {} rpc GetReservedResources (Empty) returns (ResourceList) {} rpc GetResourceOwner (Value) returns (FabricId) {} rpc GetResourceOwner (Value) returns (FabricId) {} rpc GetResourcesByType (ResourceTypeQuery) returns (TypedResourceList) {} rpc GetResourcesByType (ResourceTypeQuery) returns (TypedResourceList) {} Loading src/logical_resources/client/Logicalresourceclient.py +7 −0 Original line number Original line Diff line number Diff line Loading @@ -77,6 +77,13 @@ class LogicalResourceClient: LOGGER.debug('ReleaseResource result: {:s}'.format(grpc_message_to_json_string(response))) LOGGER.debug('ReleaseResource result: {:s}'.format(grpc_message_to_json_string(response))) return response return response @RETRY_DECORATOR def DeleteResource(self, request): LOGGER.debug('DeleteResource request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.DeleteResource(request) LOGGER.debug('DeleteResource result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR @RETRY_DECORATOR def GetReservedResources(self, request=None): def GetReservedResources(self, request=None): if request is None: if request is None: Loading src/logical_resources/database.py +7 −0 Original line number Original line Diff line number Diff line Loading @@ -73,6 +73,13 @@ class Database: self.resources[key] = '' self.resources[key] = '' return True, 'Resource released' return True, 'Resource released' def delete_resource(self, resource_type, resource_value): key = (resource_type, resource_value) if key not in self.resources: return False, 'Resource not found' del self.resources[key] return True, 'Resource deleted' def get_reserved_resources(self): def get_reserved_resources(self): return [ return [ (resource_type, resource_value, fabric_id) (resource_type, resource_value, fabric_id) Loading src/logical_resources/service/LogicalResourceServicerImpl.py +20 −1 Original line number Original line 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. import logging import logging from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.logical_resources_pb2 import ( from common.proto.logical_resources_pb2 import ( Loading Loading @@ -73,6 +87,11 @@ class LogicalResourceServicerImpl(LogicalResourcesServicer): success, message = self.db.release_resource(request.tuple.type, request.tuple.value) success, message = self.db.release_resource(request.tuple.type, request.tuple.value) return Success(success=success, message=message) return Success(success=success, message=message) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def DeleteResource(self, request, context: grpc.ServicerContext) -> Success: success, message = self.db.delete_resource(request.tuple.type, request.tuple.value) return Success(success=success, message=message) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetReservedResources(self, request, context: grpc.ServicerContext) -> ResourceList: def GetReservedResources(self, request, context: grpc.ServicerContext) -> ResourceList: reply = ResourceList() reply = ResourceList() Loading src/logical_resources/test/test_unitary.py +28 −16 Original line number Original line Diff line number Diff line Loading @@ -259,24 +259,36 @@ def test_spine_leaf_realistic_example(logical_resource_client : LogicalResourceC assert ('loopback', '10.255.0.11', 'spine-fabric') in reserved_pairs assert ('loopback', '10.255.0.11', 'spine-fabric') in reserved_pairs assert ('vlan', '100', 'leaf-fabric') in reserved_pairs assert ('vlan', '100', 'leaf-fabric') in reserved_pairs spine_owner = logical_resource_client.GetResourceOwner(Value(value='10.255.0.11')) assert spine_owner.fabric_id == 'spine-fabric' ip_resources = logical_resource_client.GetResourcesByType(ResourceTypeQuery(resource_type='ip')) def test_delete_resource(logical_resource_client: LogicalResourceClient, logical_resource_service): assert any(r.value == '10.0.0.1' for r in ip_resources.resources) add_request = ConfigParameters() assert any(r.value == '10.0.0.2' for r in ip_resources.resources) add_request.device_uuid = "leaf-delete" add_request.endpoint_uuid = "sfp-delete" add_request.ip_address = "10.10.0.10" add_request.vlan_tag = 110 add_request.asn = 65110 add_request.router_id = "10.255.10.10" add_request.vni = 1110 release_spine = logical_resource_client.ReleaseResource(ResourceReservation( add_reply = logical_resource_client.AddResources(add_request) tuple=Tuple(type='loopback', value='10.255.0.11'), assert add_reply.success is True fabric_id=FabricId(fabric_id='spine-fabric'), present_before = logical_resource_client.GetResourcesByType(ResourceTypeQuery(resource_type='ip')) assert any(r.value == '10.10.0.10' for r in present_before.resources) delete_reply = logical_resource_client.DeleteResource(ResourceReservation( tuple=Tuple(type='ip', value='10.10.0.10'), fabric_id=FabricId(fabric_id=''), )) )) assert release_spine.success is True assert delete_reply.success is True assert delete_reply.message == 'Resource deleted' available_after_release = logical_resource_client.GetAvailableResources(Empty()) present_after = logical_resource_client.GetResourcesByType(ResourceTypeQuery(resource_type='ip')) assert any(r.type == 'loopback' and r.value == '10.255.0.11' for r in available_after_release.resources) assert not any(r.value == '10.10.0.10' for r in present_after.resources) print("spine add success:", True) delete_missing = logical_resource_client.DeleteResource(ResourceReservation( print("leaf add success:", True) tuple=Tuple(type='ip', value='10.10.0.10'), print("reserved count:", len(reserved.resources)) fabric_id=FabricId(fabric_id=''), print("available count after release:", len(available_after_release.resources)) )) print("======================================\n") assert delete_missing.success is False assert delete_missing.message == 'Resource not found' Loading
proto/logical_resources.proto +1 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ service LogicalResources { rpc GetAvailableResources (Empty) returns (ResourceList) {} rpc GetAvailableResources (Empty) returns (ResourceList) {} rpc ReserveResource (ResourceReservation) returns (Success) {} rpc ReserveResource (ResourceReservation) returns (Success) {} rpc ReleaseResource (ResourceReservation) returns (Success) {} rpc ReleaseResource (ResourceReservation) returns (Success) {} rpc DeleteResource (ResourceReservation) returns (Success) {} rpc GetReservedResources (Empty) returns (ResourceList) {} rpc GetReservedResources (Empty) returns (ResourceList) {} rpc GetResourceOwner (Value) returns (FabricId) {} rpc GetResourceOwner (Value) returns (FabricId) {} rpc GetResourcesByType (ResourceTypeQuery) returns (TypedResourceList) {} rpc GetResourcesByType (ResourceTypeQuery) returns (TypedResourceList) {} Loading
src/logical_resources/client/Logicalresourceclient.py +7 −0 Original line number Original line Diff line number Diff line Loading @@ -77,6 +77,13 @@ class LogicalResourceClient: LOGGER.debug('ReleaseResource result: {:s}'.format(grpc_message_to_json_string(response))) LOGGER.debug('ReleaseResource result: {:s}'.format(grpc_message_to_json_string(response))) return response return response @RETRY_DECORATOR def DeleteResource(self, request): LOGGER.debug('DeleteResource request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.DeleteResource(request) LOGGER.debug('DeleteResource result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR @RETRY_DECORATOR def GetReservedResources(self, request=None): def GetReservedResources(self, request=None): if request is None: if request is None: Loading
src/logical_resources/database.py +7 −0 Original line number Original line Diff line number Diff line Loading @@ -73,6 +73,13 @@ class Database: self.resources[key] = '' self.resources[key] = '' return True, 'Resource released' return True, 'Resource released' def delete_resource(self, resource_type, resource_value): key = (resource_type, resource_value) if key not in self.resources: return False, 'Resource not found' del self.resources[key] return True, 'Resource deleted' def get_reserved_resources(self): def get_reserved_resources(self): return [ return [ (resource_type, resource_value, fabric_id) (resource_type, resource_value, fabric_id) Loading
src/logical_resources/service/LogicalResourceServicerImpl.py +20 −1 Original line number Original line 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. import logging import logging from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.logical_resources_pb2 import ( from common.proto.logical_resources_pb2 import ( Loading Loading @@ -73,6 +87,11 @@ class LogicalResourceServicerImpl(LogicalResourcesServicer): success, message = self.db.release_resource(request.tuple.type, request.tuple.value) success, message = self.db.release_resource(request.tuple.type, request.tuple.value) return Success(success=success, message=message) return Success(success=success, message=message) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def DeleteResource(self, request, context: grpc.ServicerContext) -> Success: success, message = self.db.delete_resource(request.tuple.type, request.tuple.value) return Success(success=success, message=message) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetReservedResources(self, request, context: grpc.ServicerContext) -> ResourceList: def GetReservedResources(self, request, context: grpc.ServicerContext) -> ResourceList: reply = ResourceList() reply = ResourceList() Loading
src/logical_resources/test/test_unitary.py +28 −16 Original line number Original line Diff line number Diff line Loading @@ -259,24 +259,36 @@ def test_spine_leaf_realistic_example(logical_resource_client : LogicalResourceC assert ('loopback', '10.255.0.11', 'spine-fabric') in reserved_pairs assert ('loopback', '10.255.0.11', 'spine-fabric') in reserved_pairs assert ('vlan', '100', 'leaf-fabric') in reserved_pairs assert ('vlan', '100', 'leaf-fabric') in reserved_pairs spine_owner = logical_resource_client.GetResourceOwner(Value(value='10.255.0.11')) assert spine_owner.fabric_id == 'spine-fabric' ip_resources = logical_resource_client.GetResourcesByType(ResourceTypeQuery(resource_type='ip')) def test_delete_resource(logical_resource_client: LogicalResourceClient, logical_resource_service): assert any(r.value == '10.0.0.1' for r in ip_resources.resources) add_request = ConfigParameters() assert any(r.value == '10.0.0.2' for r in ip_resources.resources) add_request.device_uuid = "leaf-delete" add_request.endpoint_uuid = "sfp-delete" add_request.ip_address = "10.10.0.10" add_request.vlan_tag = 110 add_request.asn = 65110 add_request.router_id = "10.255.10.10" add_request.vni = 1110 release_spine = logical_resource_client.ReleaseResource(ResourceReservation( add_reply = logical_resource_client.AddResources(add_request) tuple=Tuple(type='loopback', value='10.255.0.11'), assert add_reply.success is True fabric_id=FabricId(fabric_id='spine-fabric'), present_before = logical_resource_client.GetResourcesByType(ResourceTypeQuery(resource_type='ip')) assert any(r.value == '10.10.0.10' for r in present_before.resources) delete_reply = logical_resource_client.DeleteResource(ResourceReservation( tuple=Tuple(type='ip', value='10.10.0.10'), fabric_id=FabricId(fabric_id=''), )) )) assert release_spine.success is True assert delete_reply.success is True assert delete_reply.message == 'Resource deleted' available_after_release = logical_resource_client.GetAvailableResources(Empty()) present_after = logical_resource_client.GetResourcesByType(ResourceTypeQuery(resource_type='ip')) assert any(r.type == 'loopback' and r.value == '10.255.0.11' for r in available_after_release.resources) assert not any(r.value == '10.10.0.10' for r in present_after.resources) print("spine add success:", True) delete_missing = logical_resource_client.DeleteResource(ResourceReservation( print("leaf add success:", True) tuple=Tuple(type='ip', value='10.10.0.10'), print("reserved count:", len(reserved.resources)) fabric_id=FabricId(fabric_id=''), print("available count after release:", len(available_after_release.resources)) )) print("======================================\n") assert delete_missing.success is False assert delete_missing.message == 'Resource not found'