Commit de5e2e86 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

NBI component - TFS-API connector:

- Implemented Spectrum reservation endpoints.
parent e1bd9b9a
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ from common.proto.context_pb2 import (
    Empty, EventTypeEnum,
    Link, LinkEvent, LinkId, LinkIdList, LinkList,
    OpticalLink, OpticalLinkList,
    OpticalSpectrumReservation, OpticalSpectrumReservationId, OpticalSpectrumReservationList,
    OpticalSpectrumReservationStatusEnum,
    Service, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList,
    Slice, SliceEvent, SliceFilter, SliceId, SliceIdList, SliceList,
    Topology, TopologyDetails, TopologyEvent, TopologyId, TopologyIdList, TopologyList
@@ -765,3 +767,69 @@ class MockServicerImpl_Context(ContextServiceServicer):

        LOGGER.debug('[DeleteOpticalLink] reply={:s}'.format(grpc_message_to_json_string(reply)))
        return reply

    # ----- Optical Spectrum Reservation ------------------------------------------------------------------------------

    def ListOpticalSpectrumReservations(
        self, request : ContextId, context : grpc.ServicerContext
    ) -> OpticalSpectrumReservationList:
        LOGGER.debug('[ListOpticalSpectrumReservations] request={:s}'.format(grpc_message_to_json_string(request)))
        reservations = self.obj_db.get_entries('optical_spectrum_reservation[{:s}]'.format(
            str(request.context_uuid.uuid)
        ))
        reply = OpticalSpectrumReservationList(reservations=reservations)
        LOGGER.debug('[ListOpticalSpectrumReservations] reply={:s}'.format(grpc_message_to_json_string(reply)))
        return reply

    def GetOpticalSpectrumReservation(
        self, request : OpticalSpectrumReservationId, context : grpc.ServicerContext
    ) -> OpticalSpectrumReservation:
        LOGGER.debug('[GetOpticalSpectrumReservation] request={:s}'.format(grpc_message_to_json_string(request)))
        container_name = 'optical_spectrum_reservation[{:s}]'.format(str(request.context_id.context_uuid.uuid))
        reply = self.obj_db.get_entry(container_name, request.reservation_uuid.uuid, context)
        LOGGER.debug('[GetOpticalSpectrumReservation] reply={:s}'.format(grpc_message_to_json_string(reply)))
        return reply

    def SetOpticalSpectrumReservation(
        self, request : OpticalSpectrumReservation, context : grpc.ServicerContext
    ) -> OpticalSpectrumReservationId:
        LOGGER.debug('[SetOpticalSpectrumReservation] request={:s}'.format(grpc_message_to_json_string(request)))
        if request.status == OpticalSpectrumReservationStatusEnum.OPTICALSPECTRUMRESERVATIONSTATUS_UNDEFINED:
            request.status = OpticalSpectrumReservationStatusEnum.OPTICALSPECTRUMRESERVATIONSTATUS_RESERVED
        container_name = 'optical_spectrum_reservation[{:s}]'.format(
            str(request.reservation_id.context_id.context_uuid.uuid)
        )
        reply, _ = self._set(
            request, container_name, request.reservation_id.reservation_uuid.uuid, 'reservation_id', TOPIC_CONTEXT
        )
        LOGGER.debug('[SetOpticalSpectrumReservation] reply={:s}'.format(grpc_message_to_json_string(reply)))
        return reply

    def ConsumeOpticalSpectrumReservation(
        self, request : OpticalSpectrumReservation, context : grpc.ServicerContext
    ) -> OpticalSpectrumReservationId:
        LOGGER.debug('[ConsumeOpticalSpectrumReservation] request={:s}'.format(grpc_message_to_json_string(request)))
        container_name = 'optical_spectrum_reservation[{:s}]'.format(
            str(request.reservation_id.context_id.context_uuid.uuid)
        )
        reservation = self.obj_db.get_entry(container_name, request.reservation_id.reservation_uuid.uuid, context)
        reservation.status = OpticalSpectrumReservationStatusEnum.OPTICALSPECTRUMRESERVATIONSTATUS_CONSUMED
        if request.HasField('service_id'):
            reservation.service_id.CopyFrom(request.service_id)
        if request.HasField('connection_id'):
            reservation.connection_id.CopyFrom(request.connection_id)
        LOGGER.debug('[ConsumeOpticalSpectrumReservation] reply={:s}'.format(
            grpc_message_to_json_string(reservation.reservation_id)
        ))
        return reservation.reservation_id

    def ReleaseOpticalSpectrumReservation(
        self, request : OpticalSpectrumReservationId, context : grpc.ServicerContext
    ) -> Empty:
        LOGGER.debug('[ReleaseOpticalSpectrumReservation] request={:s}'.format(grpc_message_to_json_string(request)))
        container_name = 'optical_spectrum_reservation[{:s}]'.format(str(request.context_id.context_uuid.uuid))
        reservation = self.obj_db.get_entry(container_name, request.reservation_uuid.uuid, context)
        reservation.status = OpticalSpectrumReservationStatusEnum.OPTICALSPECTRUMRESERVATIONSTATUS_RELEASED
        reply = Empty()
        LOGGER.debug('[ReleaseOpticalSpectrumReservation] reply={:s}'.format(grpc_message_to_json_string(reply)))
        return reply
+68 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ from vnt_manager.client.VNTManagerClient import VNTManagerClient
from .Tools import (
    format_grpc_to_json, grpc_connection_id, grpc_context, grpc_context_id, grpc_device,
    grpc_device_id, grpc_link, grpc_link_id, grpc_policy_rule_id,
    grpc_optical_spectrum_reservation, grpc_optical_spectrum_reservation_id,
    grpc_service_id, grpc_service, grpc_slice, grpc_slice_id, grpc_topology, grpc_topology_id
)

@@ -348,6 +349,73 @@ class OpticalLink(_Resource):
    def get(self, link_uuid : str):
        return format_grpc_to_json(self.context_client.GetOpticalLink(grpc_link_id(link_uuid)))

class OpticalSpectrumReservations(_Resource):
    def get(self, context_uuid : str):
        return format_grpc_to_json(
            self.context_client.ListOpticalSpectrumReservations(grpc_context_id(context_uuid))
        )

    def post(self, context_uuid : str):
        json_requests = request.get_json()
        if 'reservations' in json_requests:
            json_requests = json_requests['reservations']
        if isinstance(json_requests, dict):
            json_requests = [json_requests]
        for reservation in json_requests:
            if context_uuid != reservation['reservation_id']['context_id']['context_uuid']['uuid']:
                raise BadRequest('Mismatching context_uuid')
        return jsonify([
            grpc_message_to_json(self.context_client.SetOpticalSpectrumReservation(
                grpc_optical_spectrum_reservation(reservation)
            ))
            for reservation in json_requests
        ])

class OpticalSpectrumReservation(_Resource):
    def get(self, context_uuid : str, reservation_uuid : str):
        return format_grpc_to_json(self.context_client.GetOpticalSpectrumReservation(
            grpc_optical_spectrum_reservation_id(context_uuid, reservation_uuid)
        ))

    def put(self, context_uuid : str, reservation_uuid : str):
        reservation = request.get_json()
        if context_uuid != reservation['reservation_id']['context_id']['context_uuid']['uuid']:
            raise BadRequest('Mismatching context_uuid')
        if reservation_uuid != reservation['reservation_id']['reservation_uuid']['uuid']:
            raise BadRequest('Mismatching reservation_uuid')
        return format_grpc_to_json(self.context_client.SetOpticalSpectrumReservation(
            grpc_optical_spectrum_reservation(reservation)
        ))

    def delete(self, context_uuid : str, reservation_uuid : str):
        return format_grpc_to_json(self.context_client.ReleaseOpticalSpectrumReservation(
            grpc_optical_spectrum_reservation_id(context_uuid, reservation_uuid)
        ))

class OpticalSpectrumReservationConsume(_Resource):
    def post(self, context_uuid : str, reservation_uuid : str):
        reservation = request.get_json()
        if reservation is None:
            reservation = {
                'reservation_id': {
                    'context_id': {'context_uuid': {'uuid': context_uuid}},
                    'reservation_uuid': {'uuid': reservation_uuid},
                },
            }
        if context_uuid != reservation['reservation_id']['context_id']['context_uuid']['uuid']:
            raise BadRequest('Mismatching context_uuid')
        if reservation_uuid != reservation['reservation_id']['reservation_uuid']['uuid']:
            raise BadRequest('Mismatching reservation_uuid')
        return format_grpc_to_json(self.context_client.ConsumeOpticalSpectrumReservation(
            grpc_optical_spectrum_reservation(reservation)
        ))

class OpticalSpectrumReservationRelease(_Resource):
    def post(self, context_uuid : str, reservation_uuid : str):
        return format_grpc_to_json(self.context_client.ReleaseOpticalSpectrumReservation(
            grpc_optical_spectrum_reservation_id(context_uuid, reservation_uuid)
        ))

class ConnectionIds(_Resource):
    def get(self, context_uuid : str, service_uuid : str):
        return format_grpc_to_json(self.context_client.ListConnectionIds(grpc_service_id(context_uuid, service_uuid)))
+10 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ from typing import Dict
from flask.json import jsonify
from common.proto.context_pb2 import (
    ConnectionId, Context, ContextId, Device, DeviceId, Link, LinkId,
    OpticalSpectrumReservation, OpticalSpectrumReservationId,
    ServiceId, Slice, SliceId, Topology, TopologyId, Service
)
from common.proto.policy_pb2 import PolicyRule, PolicyRuleId
@@ -54,6 +55,15 @@ def grpc_link_id(link_uuid):
def grpc_link(json_link : Dict):
    return Link(**json_link)

def grpc_optical_spectrum_reservation_id(context_uuid, reservation_uuid):
    return OpticalSpectrumReservationId(
        context_id=json_context_id(context_uuid),
        reservation_uuid={'uuid': reservation_uuid},
    )

def grpc_optical_spectrum_reservation(json_reservation : Dict):
    return OpticalSpectrumReservation(**json_reservation)

def grpc_service_id(context_uuid, service_uuid):
    return ServiceId(**json_service_id(service_uuid, context_id=json_context_id(context_uuid)))

+10 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ from .Resources import (
    DummyContexts,
    Link, LinkIds, Links,
    OpticalLink, OpticalLinks,
    OpticalSpectrumReservation, OpticalSpectrumReservationConsume,
    OpticalSpectrumReservationRelease, OpticalSpectrumReservations,
    PolicyRule, PolicyRuleIds, PolicyRules,
    Service, ServiceIds, Services,
    Slice, SliceIds, Slices,
@@ -62,6 +64,14 @@ _RESOURCES = [
    ('api.link',             Link,            '/link/<path:link_uuid>'),
    ('api.optical_links',    OpticalLinks,    '/optical_links'),
    ('api.optical_link',     OpticalLink,     '/optical_link/<path:link_uuid>'),
    ('api.optical_spectrum_reservations', OpticalSpectrumReservations,
     '/context/<path:context_uuid>/optical_spectrum_reservations'),
    ('api.optical_spectrum_reservation', OpticalSpectrumReservation,
     '/context/<path:context_uuid>/optical_spectrum_reservation/<path:reservation_uuid>'),
    ('api.optical_spectrum_reservation_consume', OpticalSpectrumReservationConsume,
     '/context/<path:context_uuid>/optical_spectrum_reservation/<path:reservation_uuid>/consume'),
    ('api.optical_spectrum_reservation_release', OpticalSpectrumReservationRelease,
     '/context/<path:context_uuid>/optical_spectrum_reservation/<path:reservation_uuid>/release'),

    ('api.connection_ids',   ConnectionIds,   '/context/<path:context_uuid>/service/<path:service_uuid>/connection_ids'),
    ('api.connections',      Connections,     '/context/<path:context_uuid>/service/<path:service_uuid>/connections'),
+137 −1
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ from nbi.service.NbiApplication import NbiApplication
from .PrepareTestScenario import ( # pylint: disable=unused-import
    # be careful, order of symbols is important here!
    nbi_application, context_client,
    do_rest_get_request
    do_rest_delete_request, do_rest_get_request, do_rest_post_request, do_rest_put_request
)


@@ -175,6 +175,142 @@ def test_rest_get_optical_link(
    validate_optical_link(reply)


# ----- Optical Spectrum Reservation ----------------------------------------------------------------------------------

def _optical_spectrum_reservation(context_uuid : str, reservation_uuid : str):
    return {
        'reservation_id': {
            'context_id': {'context_uuid': {'uuid': context_uuid}},
            'reservation_uuid': {'uuid': reservation_uuid},
        },
        'topology_id': {
            'context_id': {'context_uuid': {'uuid': context_uuid}},
            'topology_uuid': {'uuid': DEFAULT_TOPOLOGY_NAME},
        },
        'optical_link_ids': [
            {'link_uuid': {'uuid': 'OL:R1/502==R2/501'}},
        ],
        'band': 'c_slots',
        'n_start': 10,
        'n_end': 25,
        'required_slots': 16,
        'owner_id': 'nbi-unit-test',
        'correlation_id': reservation_uuid,
        'status': 1,
    }

def _validate_optical_spectrum_reservation(message, reservation_uuid : str, status : str):
    assert isinstance(message, dict)
    assert message['reservation_id']['reservation_uuid']['uuid'] == reservation_uuid
    assert message['band'] == 'c_slots'
    assert message['n_start'] == 10
    assert message['n_end'] == 25
    assert message['required_slots'] == 16
    assert message['status'] == status
    assert len(message['optical_link_ids']) == 1

def test_rest_optical_spectrum_reservation_lifecycle(
    nbi_application : NbiApplication    # pylint: disable=redefined-outer-name
) -> None:
    context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_NAME)
    reservation_uuid = 'reservation-nbi-a'
    reservation_uuid_quoted = urllib.parse.quote(reservation_uuid, safe='')
    reservation = _optical_spectrum_reservation(DEFAULT_CONTEXT_NAME, reservation_uuid)

    reply = do_rest_post_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservations'.format(context_uuid),
        reservation
    )
    assert isinstance(reply, list)
    assert len(reply) == 1
    assert reply[0]['reservation_uuid']['uuid'] == reservation_uuid

    reply = do_rest_get_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservations'.format(context_uuid)
    )
    assert isinstance(reply, dict)
    assert len(reply['reservations']) == 1
    _validate_optical_spectrum_reservation(
        reply['reservations'][0], reservation_uuid, status='OPTICALSPECTRUMRESERVATIONSTATUS_RESERVED'
    )

    reply = do_rest_get_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservation/{:s}'.format(
            context_uuid, reservation_uuid_quoted
        )
    )
    _validate_optical_spectrum_reservation(
        reply, reservation_uuid, status='OPTICALSPECTRUMRESERVATIONSTATUS_RESERVED'
    )

    reservation['owner_id'] = 'nbi-unit-test-updated'
    reply = do_rest_put_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservation/{:s}'.format(
            context_uuid, reservation_uuid_quoted
        ),
        reservation
    )
    assert reply['reservation_uuid']['uuid'] == reservation_uuid

    reply = do_rest_get_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservation/{:s}'.format(
            context_uuid, reservation_uuid_quoted
        )
    )
    _validate_optical_spectrum_reservation(
        reply, reservation_uuid, status='OPTICALSPECTRUMRESERVATIONSTATUS_RESERVED'
    )
    assert reply['owner_id'] == 'nbi-unit-test-updated'

    consume_request = {
        'reservation_id': reservation['reservation_id'],
        'service_id': {
            'context_id': {'context_uuid': {'uuid': DEFAULT_CONTEXT_NAME}},
            'service_uuid': {'uuid': 'SVC:R1/200==R2/200'},
        },
    }
    reply = do_rest_post_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservation/{:s}/consume'.format(
            context_uuid, reservation_uuid_quoted
        ),
        consume_request
    )
    assert reply['reservation_uuid']['uuid'] == reservation_uuid

    reply = do_rest_get_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservation/{:s}'.format(
            context_uuid, reservation_uuid_quoted
        )
    )
    _validate_optical_spectrum_reservation(
        reply, reservation_uuid, status='OPTICALSPECTRUMRESERVATIONSTATUS_CONSUMED'
    )
    assert reply['service_id']['service_uuid']['uuid'] == 'SVC:R1/200==R2/200'

    reply = do_rest_post_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservation/{:s}/release'.format(
            context_uuid, reservation_uuid_quoted
        )
    )
    assert reply == {}

    reply = do_rest_get_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservation/{:s}'.format(
            context_uuid, reservation_uuid_quoted
        )
    )
    _validate_optical_spectrum_reservation(
        reply, reservation_uuid, status='OPTICALSPECTRUMRESERVATIONSTATUS_RELEASED'
    )

    reply = do_rest_delete_request(
        '/tfs-api/context/{:s}/optical_spectrum_reservation/{:s}'.format(
            context_uuid, reservation_uuid_quoted
        )
    )
    assert reply == {}


# ----- Service --------------------------------------------------------------------------------------------------------

def test_rest_get_service_ids(