Commit 021782e4 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

QKD App:

- Fixed generation of UUIDs
- Fixed logic to handle get/delete/list apps
parent 6823a996
Loading
Loading
Loading
Loading
+6 −36
Original line number Diff line number Diff line
@@ -139,48 +139,18 @@ class AppServiceServicerImpl(AppServiceServicer):
        """
        Lists all apps in the system, including their statistics and QoS attributes.
        """
        LOGGER.debug(f"Received ListApps request: {grpc_message_to_json_string(request)}")

        try:
            apps = app_list_objs(self.db_engine, request.context_uuid.uuid)
            for app in apps.apps:
                LOGGER.debug(f"App retrieved: {grpc_message_to_json_string(app)}")

            LOGGER.debug(f"ListApps returned {len(apps.apps)} apps for context_id: {request.context_uuid.uuid}")
            return apps
        except Exception as e:
            context.set_code(grpc.StatusCode.INTERNAL)
            context.set_details("An internal error occurred while listing apps.")
            raise e
        return app_list_objs(self.db_engine, request)

    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def GetApp(self, request : AppId, context : grpc.ServicerContext) -> App:
        """
        Fetches details of a specific app based on its AppId, including QoS and performance stats.
        """
        LOGGER.debug(f"Received GetApp request: {grpc_message_to_json_string(request)}")
        try:
            app = app_get(self.db_engine, request)
            LOGGER.debug(f"GetApp found app with app_uuid: {request.app_uuid.uuid}")
            return app
        except NotFoundException as e:
            context.set_code(grpc.StatusCode.NOT_FOUND)
            context.set_details(f"App not found: {e}")
            raise e
        return app_get(self.db_engine, request)

    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def DeleteApp(self, request : AppId, context : grpc.ServicerContext) -> Empty:
        """
        Deletes an app from the system by its AppId, following ETSI compliance.
        """
        LOGGER.debug(f"Received DeleteApp request for app_uuid: {request.app_uuid.uuid}")
        try:
            app_delete(self.db_engine, request.app_uuid.uuid)
            LOGGER.debug(f"App with UUID {request.app_uuid.uuid} deleted successfully.")
            return Empty()
        except NotFoundException as e:
            context.set_code(grpc.StatusCode.NOT_FOUND)
            context.set_details(f"App not found: {e}")
            raise e

        return app_delete(self.db_engine, request)
+33 −32
Original line number Diff line number Diff line
@@ -23,9 +23,8 @@ from sqlalchemy_cockroachdb import run_transaction

from common.method_wrappers.ServiceExceptions import NotFoundException
from common.message_broker.MessageBroker import MessageBroker
from common.proto.context_pb2 import ContextId, Empty
from common.proto.qkd_app_pb2 import AppList, App, AppId
from qkd_app.service.database.uuids._Builder import get_uuid_from_string, get_uuid_random
from common.method_wrappers.ServiceExceptions import InvalidArgumentsException
from common.tools.object_factory.QKDApp import json_app_id
from common.tools.object_factory.Context import json_context_id

@@ -38,22 +37,20 @@ from .models.enums.QKDAppTypes import grpc_to_enum__qkd_app_types
LOGGER = logging.getLogger(__name__)


def app_list_objs(db_engine: Engine, context_uuid: str = None) -> AppList:
def app_list_objs(db_engine : Engine, request : ContextId) -> AppList:
    """
    Fetches a list of all QKD applications from the database. Optionally filters by context UUID.

    :param db_engine: SQLAlchemy Engine for DB connection
    :param context_uuid: UUID of the context to filter by (optional)
    :param request: Context Id containing the UUID of the context to filter by
    :return: AppList containing all apps
    """
    context_uuid = context_get_uuid(request, allow_random=False)
    def callback(session : Session) -> List[Dict]:
        query = session.query(AppModel)
        
        if context_uuid:
        query = query.filter_by(context_uuid=context_uuid)

        return [obj.dump() for obj in query.all()]

        obj_list : List[AppModel] = query.all()
        return [obj.dump() for obj in obj_list]
    apps = run_transaction(sessionmaker(bind=db_engine), callback)
    return AppList(apps=apps)

@@ -67,17 +64,21 @@ def app_get(db_engine: Engine, request: AppId) -> App:
    :return: App protobuf object
    :raises NotFoundException: If the app is not found in the database
    """
    app_uuid = app_get_uuid(request, allow_random=False)
    context_uuid,app_uuid = app_get_uuid(request, allow_random=False)

    def callback(session : Session) -> Optional[Dict]:
        obj = session.query(AppModel).filter_by(app_uuid=app_uuid).one_or_none()
        return obj.dump() if obj else None
        query = session.query(AppModel)
        query = query.filter_by(app_uuid=app_uuid)
        obj : Optional[AppModel] = query.one_or_none()
        return None if obj is None else obj.dump()

    obj = run_transaction(sessionmaker(bind=db_engine), callback)

    if not obj:
        raise NotFoundException('App', request.app_uuid.uuid, extra_details=[
            f'app_uuid generated was: {app_uuid}'
    if obj is None:
        raw_app_uuid = '{:s}/{:s}'.format(request.context_id.context_uuid.uuid, request.app_uuid.uuid)
        raise NotFoundException('App', raw_app_uuid, extra_details=[
            'context_uuid generated was: {:s}'.format(context_uuid),
            'app_uuid generated was: {:s}'.format(app_uuid),
        ])

    return App(**obj)
@@ -93,8 +94,7 @@ def app_set(db_engine: Engine, messagebroker: MessageBroker, request: App) -> Ap
    :param request: App protobuf object containing app data
    :return: AppId protobuf object representing the newly created or updated app
    """
    context_uuid = context_get_uuid(request.app_id.context_id, allow_random=False)
    app_uuid = app_get_uuid(request.app_id, allow_random=True)
    context_uuid,app_uuid = app_get_uuid(request.app_id, allow_random=True)

    # Prepare app data for insertion/update
    app_data = {
@@ -154,20 +154,21 @@ def app_get_by_server(db_engine: Engine, server_app_id: str) -> App:
    return App(**obj)


def app_delete(db_engine: Engine, app_uuid: str) -> None:
def app_delete(db_engine : Engine, request : AppId) -> Empty:
    """
    Deletes an app by its UUID from the database.

    :param db_engine: SQLAlchemy Engine for DB connection
    :param app_uuid: The UUID of the app to be deleted
    :param app_id: The UUID of the app to be deleted
    """
    def callback(session: Session) -> bool:
        app_obj = session.query(AppModel).filter_by(app_uuid=app_uuid).one_or_none()

        if app_obj is None:
            raise NotFoundException('App', app_uuid)
    _,app_uuid = app_get_uuid(request, allow_random=False)

        session.delete(app_obj)
        return True
    def callback(session : Session) -> bool:
        query = session.query(AppModel)
        query = query.filter_by(app_uuid=app_uuid)
        num_deleted = query.delete()
        return num_deleted > 0

    run_transaction(sessionmaker(bind=db_engine), callback)
    return Empty()
+37 −0
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.

from common.Constants import DEFAULT_CONTEXT_NAME
from common.proto.context_pb2 import ContextId
from common.method_wrappers.ServiceExceptions import InvalidArgumentsException
from ._Builder import get_uuid_from_string, get_uuid_random

def context_get_uuid(
    context_id : ContextId, context_name : str = '', allow_random : bool = False, allow_default : bool = False
) -> str:
    context_uuid = context_id.context_uuid.uuid

    if len(context_uuid) > 0:
        return get_uuid_from_string(context_uuid)
    if len(context_name) > 0:
        return get_uuid_from_string(context_name)
    if allow_default:
        return get_uuid_from_string(DEFAULT_CONTEXT_NAME)
    if allow_random:
        return get_uuid_random()

    raise InvalidArgumentsException([
        ('context_id.context_uuid.uuid', context_uuid),
        ('name', context_name),
    ], extra_details=['At least one is required to produce a Context UUID'])
+20 −10
Original line number Diff line number Diff line
@@ -12,26 +12,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Tuple
from common.proto.qkd_app_pb2 import AppId
from common.method_wrappers.ServiceExceptions import InvalidArgumentsException
from ._Builder import get_uuid_from_string, get_uuid_random
from .Context import context_get_uuid

def app_get_uuid(app_id: AppId, allow_random: bool = False) -> str:
def app_get_uuid(
    app_id : AppId, app_name : str = '', allow_random : bool = False
) -> Tuple[str, str]:
    """
    Retrieves or generates the UUID for an app.
    
    :param app_id: AppId object that contains the app UUID
    :param application_id: AppId object that contains the app UUID
    :param application_name: string that contains optional app name
    :param allow_random: If True, generates a random UUID if app_uuid is not set
    :return: App UUID as a string
    :return: Context UUID as a string , App UUID as a string
    """
    app_uuid = app_id.app_uuid.uuid
    context_uuid = context_get_uuid(app_id.context_id, allow_random=False, allow_default=True)
    raw_app_uuid = app_id.app_uuid.uuid

    if app_uuid:
        return get_uuid_from_string(app_uuid)
    if len(raw_app_uuid) > 0:
        return context_uuid, get_uuid_from_string(raw_app_uuid, prefix_for_name=context_uuid)

    if len(app_name) > 0:
        return context_uuid, get_uuid_from_string(app_name, prefix_for_name=context_uuid)

    if allow_random:
        return get_uuid_random()
        return context_uuid, get_uuid_random()

    raise InvalidArgumentsException([
        ('app_id.app_uuid.uuid', app_uuid),
    ], extra_details=['At least one UUID is required to identify the app.'])
        ('app_id.app_uuid.uuid', raw_app_uuid),
        ('name', app_name),
    ], extra_details=['At least one is required to produce a App UUID'])