Commit 2792d9c8 authored by Mohamad Rahhal's avatar Mohamad Rahhal
Browse files

Logical_Resources component :

- Added missing methods (partial)
parent 447601fb
Loading
Loading
Loading
Loading
+108 −28
Original line number Original line Diff line number Diff line
@@ -16,43 +16,123 @@ syntax = "proto3";
package logical_resources;
package logical_resources;


service LogicalResources {
service LogicalResources {
    rpc AddResources (ConfigParameters) returns (Response) {}
    rpc GetAvailableResource (Empty) returns (ResourceList) {}
    rpc ReserveResource (ResourceReservation) returns (Success) {}
    rpc DeleteResource (ResourceReservation) returns (Success) {}
    rpc GetAllocatedResources (Empty) returns (ResourceList) {}
    rpc GetResourceOwner (Value) returns (FabricId) {}
    rpc GetResourcesByType (ResourceTypeQuery) returns (TypedResourceList) {}

    // old ones
    rpc AddDevice (DeviceConfig) returns (Response) {}
    rpc AddDevice (DeviceConfig) returns (Response) {}
    rpc DeleteDevice (DeviceId) returns (Response) {}
    rpc DeleteDevice (DeviceId) returns (Response) {}
    rpc GetDeviceConfig (DeviceId) returns (DeviceConfig) {}
    rpc GetDeviceConfig (DeviceId) returns (DeviceConfig) {}
  rpc GetDatabase (Empty) returns (Database) {} } 
    rpc GetDatabase (Empty) returns (Database) {}
}


message Empty {}
message Empty {}

message Value {
    string value = 1;
}

message FabricId {
    string fabric_id = 1;
}

message Tuple {
    string type = 1;
    string value = 2;
}

message Success {
    bool success = 1;
    string message = 2;
}

message ResourceReservation {
    Tuple tuple = 1;
    FabricId fabric_id = 2;
}

message ResourceTypeQuery {
    string resource_type = 1;
}

message ResourceEntry {
    string type = 1;
    string value = 2;
    string fabric_id = 3;
    bool allocated = 4;
}

message ResourceList {
    repeated ResourceEntry resources = 1;
}

message TypedResourceList {
    string resource_type = 1;
    repeated ResourceEntry resources = 2;
}

message ConfigParameters {
    string device_uuid = 1 [json_name = "device-uuid"];
    string endpoint_uuid = 2 [json_name = "endpoint-uuid"];
    string ip_address = 3 [json_name = "ip-address"];
    uint32 vlan_tag = 4 [json_name = "vlan-tag"];
    string mac_address = 5 [json_name = "mac-address"];
    uint32 asn = 6;
    string router_id = 7 [json_name = "router-id"];
    uint32 vni = 8;
    string local_address = 9 [json_name = "local.address"];
    uint32 port = 10;
    string bridge = 11;
    string interface = 12;
    string remote_address = 13 [json_name = "remote.address"];
    uint32 remote_as = 14 [json_name = "remote.as"];
    string local_role = 15 [json_name = "local.role"];
    string routing_table = 16 [json_name = "routing-table"];
    bool multihop = 17;
    string afi = 18 [json_name = "address-families"];
    string name = 19;
}

message DeviceConfig {
message DeviceConfig {
string device_uuid = 1;
    string device_uuid = 1 [json_name = "device-uuid"];
string endpoint_uuid = 2;
    string endpoint_uuid = 2 [json_name = "endpoint-uuid"];
string ip_address = 3;
    string ip_address = 3 [json_name = "ip-address"];
uint32 vlan_tag = 4;
    uint32 vlan_tag = 4 [json_name = "vlan-tag"];
string mac_address = 5;
    string mac_address = 5 [json_name = "mac-address"];
    uint32 asn = 6;
    uint32 asn = 6;
string router_id = 7;
    string router_id = 7 [json_name = "router-id"];
    uint32 vni = 8;
    uint32 vni = 8;
string local_address = 9;
    string local_address = 9 [json_name = "local.address"];
    uint32 port = 10;
    uint32 port = 10;
    string bridge = 11;
    string bridge = 11;
    string interface = 12;
    string interface = 12;
string remote_address = 13;
    string remote_address = 13 [json_name = "remote.address"];
uint32 remote_as = 14;
    uint32 remote_as = 14 [json_name = "remote.as"];
string local_role = 15;
    string local_role = 15 [json_name = "local.role"];
string routing_table = 16;
    string routing_table = 16 [json_name = "routing-table"];
    bool multihop = 17;
    bool multihop = 17;
string afi = 18;
    string afi = 18 [json_name = "address-families"];
    string name = 19;
    string name = 19;
}
}


message DeviceId {
message DeviceId {
    string device_uuid = 1;} 
    string device_uuid = 1;
}


message Response {
message Response {
    bool success = 1;
    bool success = 1;
    string message = 2;}
    string message = 2;
}


message Database {
message Database {
    map<string, DeviceEndpoints> devices = 1;}
    map<string, DeviceEndpoints> devices = 1;
}


message DeviceEndpoints {
message DeviceEndpoints {
    map<string, DeviceConfig> endpoints = 1;}
    map<string, DeviceConfig> endpoints = 1;
 No newline at end of file
}
 No newline at end of file
+80 −7
Original line number Original line Diff line number Diff line
@@ -15,7 +15,10 @@
import grpc, logging
import grpc, logging
from common.Constants import ServiceNameEnum
from common.Constants import ServiceNameEnum
from common.Settings import get_service_host, get_service_port_grpc
from common.Settings import get_service_host, get_service_port_grpc
from common.proto.logical_resources_pb2 import Response, DeviceConfig, Database, Empty
from common.proto.logical_resources_pb2 import (
    ConfigParameters,
    Empty,
)
from common.proto.logical_resources_pb2_grpc import LogicalResourcesStub
from common.proto.logical_resources_pb2_grpc import LogicalResourcesStub
from common.tools.client.RetryDecorator import retry, delay_exponential
from common.tools.client.RetryDecorator import retry, delay_exponential
from common.tools.grpc.Tools import grpc_message_to_json_string
from common.tools.grpc.Tools import grpc_message_to_json_string
@@ -46,17 +49,87 @@ class LogicalResourceClient:
        self.stub = None
        self.stub = None


    @RETRY_DECORATOR
    @RETRY_DECORATOR
    def AddDevice(self, request: DeviceConfig)-> Response:
    def AddResources(self, request):
        LOGGER.debug('ADD device request: {:s}'.format(grpc_message_to_json_string(request)))
        LOGGER.debug('AddResources request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.AddDevice(request)
        response = self.stub.AddResources(request)
        LOGGER.debug('add device result: {:s}'.format(grpc_message_to_json_string(response)))
        LOGGER.debug('AddResources result: {:s}'.format(grpc_message_to_json_string(response)))
        return response
        return response


    @RETRY_DECORATOR
    @RETRY_DECORATOR
    def GetDatabase(self, request: Empty = None) -> Database:
    def GetAvailableResource(self, request=None):
        if request is None:
            request = Empty()
        LOGGER.debug('GetAvailableResource request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.GetAvailableResource(request)
        LOGGER.debug('GetAvailableResource result: {:s}'.format(grpc_message_to_json_string(response)))
        return response

    @RETRY_DECORATOR
    def ReserveResource(self, request):
        LOGGER.debug('ReserveResource request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.ReserveResource(request)
        LOGGER.debug('ReserveResource result: {:s}'.format(grpc_message_to_json_string(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
    def GetAllocatedResources(self, request=None):
        if request is None:
            request = Empty()
        LOGGER.debug('GetAllocatedResources request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.GetAllocatedResources(request)
        LOGGER.debug('GetAllocatedResources result: {:s}'.format(grpc_message_to_json_string(response)))
        return response

    @RETRY_DECORATOR
    def GetResourceOwner(self, request):
        LOGGER.debug('GetResourceOwner request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.GetResourceOwner(request)
        LOGGER.debug('GetResourceOwner result: {:s}'.format(grpc_message_to_json_string(response)))
        return response

    @RETRY_DECORATOR
    def GetResourcesByType(self, request):
        LOGGER.debug('GetResourcesByType request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.GetResourcesByType(request)
        LOGGER.debug('GetResourcesByType result: {:s}'.format(grpc_message_to_json_string(response)))
        return response

    @RETRY_DECORATOR
    def GetDatabase(self, request=None):
        if request is None:
        if request is None:
            request = Empty()
            request = Empty()
        LOGGER.debug('Get database request: {:s}'.format(grpc_message_to_json_string(request)))
        LOGGER.debug('Get database request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.stub.GetDatabase(request)
        response = self.stub.GetDatabase(request)
        LOGGER.debug('get database result: {:s}'.format(grpc_message_to_json_string(response)))
        LOGGER.debug('get database result: {:s}'.format(grpc_message_to_json_string(response)))
        return response
        return response

    @RETRY_DECORATOR
    def AddDevice(self, request):
        return self.AddResources(ConfigParameters(
            device_uuid=request.device_uuid,
            endpoint_uuid=request.endpoint_uuid,
            ip_address=request.ip_address,
            vlan_tag=request.vlan_tag,
            mac_address=request.mac_address,
            asn=request.asn,
            router_id=request.router_id,
            vni=request.vni,
            local_address=request.local_address,
            port=request.port,
            bridge=request.bridge,
            interface=request.interface,
            remote_address=request.remote_address,
            remote_as=request.remote_as,
            local_role=request.local_role,
            routing_table=request.routing_table,
            multihop=request.multihop,
            afi=request.afi,
            name=request.name,
        ))
 No newline at end of file
+83 −3
Original line number Original line Diff line number Diff line
@@ -3,8 +3,14 @@ class Database:
    def __init__(self):
    def __init__(self):
        "Creating the database to store things"
        "Creating the database to store things"
        self.database = {}
        self.database = {}
        self.resources = {}


    def add_device(self, device_uuid, endpoint_uuid, ip_address, vlan_tag, mac_address, ASN, RouterID,
    def _track_resource(self, resource_type, resource_value):
        if not resource_value:
            return
        self.resources.setdefault((resource_type, resource_value), '')

    def add_resources(self, device_uuid, endpoint_uuid, ip_address, vlan_tag, mac_address, ASN, RouterID,
                      vni=None, local_address=None, port=None, bridge=None, interface=None, remote_address=None,
                      vni=None, local_address=None, port=None, bridge=None, interface=None, remote_address=None,
                      remote_as=None, local_role=None, routing_table=None, multihop=None, afi=None, name=None):
                      remote_as=None, local_role=None, routing_table=None, multihop=None, afi=None, name=None):
        if device_uuid not in self.database:
        if device_uuid not in self.database:
@@ -35,10 +41,84 @@ class Database:
        if vlan_tag:
        if vlan_tag:
            self.database[device_uuid][endpoint_uuid]["vlan_tags"].append(vlan_tag)
            self.database[device_uuid][endpoint_uuid]["vlan_tags"].append(vlan_tag)


        self._track_resource('ip', ip_address)
        self._track_resource('vlan', str(vlan_tag) if vlan_tag else '')
        self._track_resource('asn', str(ASN) if ASN else '')
        self._track_resource('loopback', RouterID)

    # Backward-compatible alias kept while callers migrate to add_resources.
    def add_device(self, device_uuid, endpoint_uuid, ip_address, vlan_tag, mac_address, ASN, RouterID,
                   vni=None, local_address=None, port=None, bridge=None, interface=None, remote_address=None,
                   remote_as=None, local_role=None, routing_table=None, multihop=None, afi=None, name=None):
        self.add_resources(
            device_uuid,
            endpoint_uuid,
            ip_address,
            vlan_tag,
            mac_address,
            ASN,
            RouterID,
            vni=vni,
            local_address=local_address,
            port=port,
            bridge=bridge,
            interface=interface,
            remote_address=remote_address,
            remote_as=remote_as,
            local_role=local_role,
            routing_table=routing_table,
            multihop=multihop,
            afi=afi,
            name=name,
        )

    def get_full_db(self):
    def get_full_db(self):
        "Return ALLL"
        "Return ALLL"
        return self.database
        return self.database


    def get_available_resources(self):
        return [
            (resource_type, resource_value, fabric_id)
            for (resource_type, resource_value), fabric_id in self.resources.items()
            if not fabric_id
        ]

    def reserve_resource(self, resource_type, resource_value, fabric_id):
        key = (resource_type, resource_value)
        if key not in self.resources:
            return False, 'Resource not found'
        if self.resources[key]:
            return False, 'Resource already reserved'
        self.resources[key] = fabric_id
        return True, 'Resource reserved'

    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_allocated_resources(self):
        return [
            (resource_type, resource_value, fabric_id)
            for (resource_type, resource_value), fabric_id in self.resources.items()
            if fabric_id
        ]

    def get_resource_owner(self, resource_value):
        for (_, value), fabric_id in self.resources.items():
            if value == resource_value:
                return fabric_id
        return ''

    def get_resources_by_type(self, resource_type):
        return [
            (rtype, value, fabric_id)
            for (rtype, value), fabric_id in self.resources.items()
            if rtype == resource_type
        ]
    
# --- DELETE ---
# --- DELETE ---
    def delete_device(self, device_uuid):
    def delete_device(self, device_uuid):
        """to remove the entire device"""
        """to remove the entire device"""
+93 −4
Original line number Original line Diff line number Diff line
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 Response, DeviceConfig, Database, DeviceEndpoints
from common.proto.logical_resources_pb2 import (
    Response,
    Success,
    ConfigParameters,
    DeviceConfig,
    Database,
    DeviceEndpoints,
    ResourceList,
    ResourceEntry,
    FabricId,
    TypedResourceList,
)
from common.proto.logical_resources_pb2_grpc import LogicalResourcesServicer
from common.proto.logical_resources_pb2_grpc import LogicalResourcesServicer
from logical_resources.database import Database as InternalDatabase 
from logical_resources.database import Database as InternalDatabase 
import grpc
import grpc
@@ -15,8 +26,8 @@ class LogicalResourceServicerImpl(LogicalResourcesServicer):
        LOGGER.debug('Servicer Created')
        LOGGER.debug('Servicer Created')


    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def AddDevice(self, request: DeviceConfig, context : grpc.ServicerContext) -> Response:
    def AddResources(self, request: ConfigParameters, context : grpc.ServicerContext) -> Response:
        self.db.add_device(
        self.db.add_resources(
            request.device_uuid,
            request.device_uuid,
            request.endpoint_uuid,
            request.endpoint_uuid,
            request.ip_address,
            request.ip_address,
@@ -37,7 +48,85 @@ class LogicalResourceServicerImpl(LogicalResourcesServicer):
            request.afi,
            request.afi,
            request.name
            request.name
        )
        )
        return Response(success=True, message="Device added to Logical Database")
        return Response(success=True, message='Resources added to Logical Database')

    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def GetAvailableResource(self, request, context: grpc.ServicerContext) -> ResourceList:
        reply = ResourceList()
        for resource_type, resource_value, fabric_id in self.db.get_available_resources():
            reply.resources.append(ResourceEntry(
                type=resource_type,
                value=resource_value,
                fabric_id='',
                allocated=False,
            ))
        return reply

    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def ReserveResource(self, request, context: grpc.ServicerContext) -> Success:
        success, message = self.db.reserve_resource(
            request.tuple.type,
            request.tuple.value,
            request.fabric_id.fabric_id,
        )
        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)
    def GetAllocatedResources(self, request, context: grpc.ServicerContext) -> ResourceList:
        reply = ResourceList()
        for resource_type, resource_value, fabric_id in self.db.get_allocated_resources():
            reply.resources.append(ResourceEntry(
                type=resource_type,
                value=resource_value,
                fabric_id=fabric_id,
                allocated=True,
            ))
        return reply

    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def GetResourceOwner(self, request, context: grpc.ServicerContext) -> FabricId:
        return FabricId(fabric_id=self.db.get_resource_owner(request.value))

    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def GetResourcesByType(self, request, context: grpc.ServicerContext) -> TypedResourceList:
        reply = TypedResourceList(resource_type=request.resource_type)
        for resource_type, resource_value, fabric_id in self.db.get_resources_by_type(request.resource_type):
            reply.resources.append(ResourceEntry(
                type=resource_type,
                value=resource_value,
                fabric_id=fabric_id,
                allocated=bool(fabric_id),
            ))
        return reply

    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def AddDevice(self, request: DeviceConfig, context : grpc.ServicerContext) -> Response:
        return self.AddResources(ConfigParameters(
            device_uuid=request.device_uuid,
            endpoint_uuid=request.endpoint_uuid,
            ip_address=request.ip_address,
            vlan_tag=request.vlan_tag,
            mac_address=request.mac_address,
            asn=request.asn,
            router_id=request.router_id,
            vni=request.vni,
            local_address=request.local_address,
            port=request.port,
            bridge=request.bridge,
            interface=request.interface,
            remote_address=request.remote_address,
            remote_as=request.remote_as,
            local_role=request.local_role,
            routing_table=request.routing_table,
            multihop=request.multihop,
            afi=request.afi,
            name=request.name,
        ), context)


    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def GetDatabase(self, request, context:grpc.ServicerContext) -> Database:
    def GetDatabase(self, request, context:grpc.ServicerContext) -> Database:
+66 −32
Original line number Original line Diff line number Diff line
@@ -12,9 +12,16 @@
# See the License for the specific language governing permissions and
# See the License for the specific language governing permissions and
# limitations under the License.
# limitations under the License.


import pytest
from logical_resources.client.Logicalresourceclient import LogicalResourceClient
from logical_resources.client.Logicalresourceclient import LogicalResourceClient
from common.proto.logical_resources_pb2 import DeviceConfig, Empty
from common.proto.logical_resources_pb2 import (
    ConfigParameters,
    Empty,
    ResourceReservation,
    Tuple,
    FabricId,
    Value,
    ResourceTypeQuery,
)




from .PrepareTestScenario import (
from .PrepareTestScenario import (
@@ -24,7 +31,7 @@ from .PrepareTestScenario import (


def test_add(logical_resource_client : LogicalResourceClient, logical_resource_service):
def test_add(logical_resource_client : LogicalResourceClient, logical_resource_service):


    add_request = DeviceConfig()
    add_request = ConfigParameters()
    add_request.device_uuid = "leaf1"
    add_request.device_uuid = "leaf1"
    add_request.endpoint_uuid = "sfp1"
    add_request.endpoint_uuid = "sfp1"
    add_request.ip_address = "10.0.0.1"
    add_request.ip_address = "10.0.0.1"
@@ -44,9 +51,30 @@ def test_add(logical_resource_client : LogicalResourceClient, logical_resource_s
    add_request.multihop = True
    add_request.multihop = True
    add_request.afi = "ip,l2vpn"
    add_request.afi = "ip,l2vpn"
    add_request.name = "test-device"
    add_request.name = "test-device"
    add_reply = logical_resource_client.AddDevice(add_request)
    add_reply = logical_resource_client.AddResources(add_request)
    assert add_reply.success == True
    assert add_reply.success == True


    available = logical_resource_client.GetAvailableResource(Empty())
    assert any(r.type == 'ip' and r.value == '10.0.0.1' for r in available.resources)
    assert any(r.type == 'vlan' and r.value == '100' for r in available.resources)
    assert any(r.type == 'asn' and r.value == '65001' for r in available.resources)
    assert any(r.type == 'loopback' and r.value == '1.1.1.1' for r in available.resources)

    # Validate key underlay/overlay fields are persisted (MikroTik-style mapping).
    db_reply = logical_resource_client.GetDatabase(Empty())
    endpoint = db_reply.devices['leaf1'].endpoints['sfp1']
    assert endpoint.local_role == 'ebgp'
    assert endpoint.routing_table == 'vrf-dataplane'
    assert endpoint.multihop is True
    assert endpoint.afi == 'ip,l2vpn'
    assert endpoint.vni == 10
    assert endpoint.port == 4789
    assert endpoint.bridge == 'br-dataplane'
    assert endpoint.interface == 'vxlan10'
    assert endpoint.local_address == '192.168.1.1'
    assert endpoint.remote_address == '10.0.0.2'
    assert endpoint.remote_as == 65002
    
    # to print what is stored in the datbase after the add operation
    # to print what is stored in the datbase after the add operation
    import json
    import json
    stored_data = logical_resource_service.servicer_impl.db.get_full_db()
    stored_data = logical_resource_service.servicer_impl.db.get_full_db()
@@ -54,9 +82,9 @@ def test_add(logical_resource_client : LogicalResourceClient, logical_resource_s
    print(json.dumps(stored_data, indent=2))
    print(json.dumps(stored_data, indent=2))
    print("==================================\n")
    print("==================================\n")


def test_get_database(logical_resource_client : LogicalResourceClient, logical_resource_service):
def test_resource_allocation_and_queries(logical_resource_client : LogicalResourceClient, logical_resource_service):


    add_request = DeviceConfig()
    add_request = ConfigParameters()
    add_request.device_uuid = "leaf2"
    add_request.device_uuid = "leaf2"
    add_request.endpoint_uuid = "sfp2"
    add_request.endpoint_uuid = "sfp2"
    add_request.ip_address = "10.0.0.2"
    add_request.ip_address = "10.0.0.2"
@@ -76,34 +104,40 @@ def test_get_database(logical_resource_client : LogicalResourceClient, logical_r
    add_request.multihop = False
    add_request.multihop = False
    add_request.afi = "ip"
    add_request.afi = "ip"
    add_request.name = "test-device2"
    add_request.name = "test-device2"
    add_reply = logical_resource_client.AddDevice(add_request)
    add_reply = logical_resource_client.AddResources(add_request)
    assert add_reply.success is True
    assert add_reply.success is True


    get_request = Empty()
    reserve_reply = logical_resource_client.ReserveResource(ResourceReservation(
    get_reply = logical_resource_client.GetDatabase(get_request)
        tuple=Tuple(type='ip', value='10.0.0.2'),
    assert get_reply is not None
        fabric_id=FabricId(fabric_id='fabric-A'),
    assert "leaf2" in get_reply.devices
    ))
    assert "sfp2" in get_reply.devices["leaf2"].endpoints
    assert reserve_reply.success is True
    endpoint_cfg = get_reply.devices["leaf2"].endpoints["sfp2"]

    assert endpoint_cfg.device_uuid == "leaf2"
    available_after_reserve = logical_resource_client.GetAvailableResource(Empty())
    assert endpoint_cfg.endpoint_uuid == "sfp2"
    assert not any(r.type == 'ip' and r.value == '10.0.0.2' for r in available_after_reserve.resources)
    assert endpoint_cfg.ip_address == "10.0.0.2"

    assert endpoint_cfg.vlan_tag == 200
    allocated = logical_resource_client.GetAllocatedResources(Empty())
    assert endpoint_cfg.mac_address == "AA:BB:CC:DD:EE:01"
    assert any(r.type == 'ip' and r.value == '10.0.0.2' and r.fabric_id == 'fabric-A' for r in allocated.resources)
    assert endpoint_cfg.asn == 65002

    assert endpoint_cfg.router_id == "2.2.2.2"
    owner = logical_resource_client.GetResourceOwner(Value(value='10.0.0.2'))
    assert endpoint_cfg.vni == 20
    assert owner.fabric_id == 'fabric-A'
    assert endpoint_cfg.local_address == "192.168.2.1"

    assert endpoint_cfg.port == 4790
    typed = logical_resource_client.GetResourcesByType(ResourceTypeQuery(resource_type='ip'))
    assert endpoint_cfg.bridge == "br-dataplane2"
    assert typed.resource_type == 'ip'
    assert endpoint_cfg.interface == "vxlan20"
    assert any(r.value == '10.0.0.2' for r in typed.resources)
    assert endpoint_cfg.remote_address == "10.0.0.3"

    assert endpoint_cfg.remote_as == 65003
    delete_reply = logical_resource_client.DeleteResource(ResourceReservation(
    assert endpoint_cfg.local_role == "ibgp"
        tuple=Tuple(type='ip', value='10.0.0.2'),
    assert endpoint_cfg.routing_table == "vrf-dataplane2"
        fabric_id=FabricId(fabric_id='fabric-A'),
    assert endpoint_cfg.multihop == False
    ))
    assert endpoint_cfg.afi == "ip"
    assert delete_reply.success is True
    assert endpoint_cfg.name == "test-device2"

    allocated_after_delete = logical_resource_client.GetAllocatedResources(Empty())
    assert not any(r.type == 'ip' and r.value == '10.0.0.2' for r in allocated_after_delete.resources)

    owner_after_delete = logical_resource_client.GetResourceOwner(Value(value='10.0.0.2'))
    assert owner_after_delete.fabric_id == ''

    # to print what is stored in the datbase after the get operation
    # to print what is stored in the datbase after the get operation
    import json
    import json
    stored_data = logical_resource_service.servicer_impl.db.get_full_db()
    stored_data = logical_resource_service.servicer_impl.db.get_full_db()