Commit bcbffd85 authored by Adriana Fernández-Fernández's avatar Adriana Fernández-Fernández
Browse files

Adapts application deployment management API

parent 9f11e8e9
Loading
Loading
Loading
Loading
+299 −0
Original line number Diff line number Diff line
# -------------------------------------------------------------------------- #
# Copyright 2025-present, Federation Manager, by Software Networks, i2CAT    #
#                                                                            #
# 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 mongoengine.errors import ValidationError

from models.mongo_document import OriginatingOperatorPlatformOriginatingOP
from models.mongo_document import OriginatingApplicationDeploymentManagementOriginatingOP
from adapters.error import APIError
from clients import fed_manager as fm_client


def verify_required_header(partner_api_root):
    if not partner_api_root:
        raise APIError(400, "X-Partner-API-Root header is missing")


def get_all_app_instances(federation_context_id, app_id, app_provider_id, bearer_token, partner_api_root):  # noqa: E501
    """Retrieves all application instance of partner OP

     # noqa: E501

    :param federation_context_id:
    :type federation_context_id: dict | bytes
    :param app_id:
    :type app_id: dict | bytes
    :param app_provider_id:
    :type app_provider_id: dict | bytes

    :rtype: List[InlineResponse2009]
    """

    # Verify partner API root is provided
    verify_required_header(partner_api_root)

    try:
        # Find federation at originating OP
        originating_op_objects = OriginatingOperatorPlatformOriginatingOP.objects(id=federation_context_id)
        if not originating_op_objects:
            raise APIError(404, "Federation not found")
        originating_op_instance = originating_op_objects[0]
        
        # Find application deployment at partner
        deployment_response_data = fm_client.get_all_instances_deployment(
            originating_op_instance.partner_federation_id, app_id, app_provider_id, bearer_token, partner_api_root)
        
        # Check if the response contains an error from the partner API
        if "error" in deployment_response_data and "status_code" in deployment_response_data:
            status_code = deployment_response_data["status_code"]
            error_message = deployment_response_data["error"]
            raise APIError(status_code, f"Partner API error: {error_message}")
        
        info = ""
        if len(deployment_response_data) > 0:
            try:
                info = deployment_response_data[0]
            except:
                info = ""
        if "appInstanceInfo" in info:
            # Find all instances in originating operator
            if find_all_instances_originating_op(federation_context_id, app_id, app_provider_id):
               return deployment_response_data, 200
            else:
                raise APIError(409, "Application deployment exists at partner operator but not in originating operator")
        else:
            if find_all_instances_originating_op(federation_context_id, app_id, app_provider_id):
                raise APIError(409, "Application deployment exists in originating operator but not at partner operator")
            else:
                raise APIError(404, "Application deployment not found")
    except ValidationError:
        raise APIError(400, f"Invalid federation context ID format: {federation_context_id}")
    except APIError:
        raise  # Re-raise APIError as-is
    except Exception as error:
        raise APIError(500, f"Error while getting all app instances. Reason: {error}")


def get_app_instance_details(federation_context_id, app_id, app_instance_id, zone_id, bearer_token, partner_api_root):  # noqa: E501
    """Retrieves an application instance details from partner OP.

     # noqa: E501

    :param federation_context_id:
    :type federation_context_id: dict | bytes
    :param app_id:
    :type app_id: dict | bytes
    :param app_instance_id:
    :type app_instance_id: dict | bytes
    :param zone_id:
    :type zone_id: dict | bytes

    :rtype: InlineResponse2008
    """

    # Verify partner API root is provided
    verify_required_header(partner_api_root)

    try:
        # Find federation at originating OP
        originating_op_objects = OriginatingOperatorPlatformOriginatingOP.objects(id=federation_context_id)
        if not originating_op_objects:
            raise APIError(404, "Federation not found")
        originating_op_instance = originating_op_objects[0]
        
        # Find application deployment details at partner
        deployment_response_data = fm_client.get_instance_details_deployment(
            originating_op_instance.partner_federation_id, app_id, app_instance_id, zone_id, bearer_token,
            partner_api_root)
        
        # Check if the response contains an error from the partner API
        if "error" in deployment_response_data and "status_code" in deployment_response_data:
            status_code = deployment_response_data["status_code"]
            error_message = deployment_response_data["error"]
            raise APIError(status_code, f"Partner API error: {error_message}")
        elif "appInstanceState" in deployment_response_data:
            # Find instance details in originating operator
            if find_instance_details(federation_context_id, app_id, app_instance_id, zone_id):
                return deployment_response_data, 200
            else:
                raise APIError(409, "Application Deployment exist at partner operator but not in originating operator")
        else:
            if find_instance_details(federation_context_id, app_id, app_instance_id, zone_id):
                raise APIError(409, "Application Deployment exist in originating operator but not at partner operator")
            else:
                raise APIError(404, "Application Deployment not found")
    except ValidationError:
        raise APIError(400, f"Invalid federation context ID format: {federation_context_id}")
    except APIError:
        raise  # Re-raise APIError as-is
    except Exception as error:
        raise APIError(500, f"Error while getting app instance details. Reason: {error}")


def install_app(federation_context_id, body, bearer_token, partner_api_root):  # noqa: E501
    """Instantiates an application on a partner OP zone.

     # noqa: E501

    :param federation_context_id:
    :type federation_context_id: dict | bytes
    :param body: Details about application and zones where application instance should be created. It also definea call back URI which the partner OP shall use update home OP about a change in instance status.
    :type body: dict | bytes

    :rtype: InlineResponse202
    """

    # Verify partner API root is provided
    verify_required_header(partner_api_root)

    try:
        # Find federation at originating OP
        originating_op_objects = OriginatingOperatorPlatformOriginatingOP.objects(id=federation_context_id)
        if not originating_op_objects:
            raise APIError(404, "Federation not found")
        originating_op_instance = originating_op_objects[0]

        # Create deployment at partner
        deployment_response_data = fm_client.install_app_deployment(originating_op_instance.partner_federation_id,
                                                                    body, bearer_token,
                                                                    partner_api_root)
        
        # Check if the response contains an error from the partner API
        if "error" in deployment_response_data and "status_code" in deployment_response_data:
            status_code = deployment_response_data["status_code"]
            error_message = deployment_response_data["error"]
            raise APIError(status_code, f"Partner API error: {error_message}")
        elif "appInstIdentifier" in deployment_response_data:
            install_app_originating_op(federation_context_id, originating_op_instance.partner_federation_id, body,
                                       deployment_response_data)
            return deployment_response_data, 202
        else:
            raise APIError(422, f"Unexpected response from partner API: {deployment_response_data}")
    except ValidationError:
        raise APIError(400, f"Invalid federation context ID format: {federation_context_id}")
    except APIError:
        raise  # Re-raise APIError as-is
    except Exception as error:
        raise APIError(500, f"Error installing application. Reason: {error}")


def remove_app(federation_context_id, app_id, app_instance_id, zone_id, bearer_token, partner_api_root):  # noqa: E501
    """Terminate an application instance on a partner OP zone.

     # noqa: E501

    :param federation_context_id:
    :type federation_context_id: dict | bytes
    :param app_id:
    :type app_id: dict | bytes
    :param app_instance_id:
    :type app_instance_id: dict | bytes
    :param zone_id:
    :type zone_id: dict | bytes

    :rtype: None
    """

    # Verify partner API root is provided
    verify_required_header(partner_api_root)

    try:
        # Find federation at originating OP
        originating_op_objects = OriginatingOperatorPlatformOriginatingOP.objects(id=federation_context_id)
        if not originating_op_objects:
            raise APIError(404, "Federation not found")
        originating_op_instance = originating_op_objects[0]
        
        # Find application deployment details at partner
        deployment_response_data = fm_client.get_instance_details_deployment(
            originating_op_instance.partner_federation_id, app_id, app_instance_id, zone_id, bearer_token,
            partner_api_root)

        # Check if the response contains an error from the partner API
        if "error" in deployment_response_data and "status_code" in deployment_response_data:
            status_code = deployment_response_data["status_code"]
            error_message = deployment_response_data["error"]
            raise APIError(status_code, f"Partner API error: {error_message}")
        elif "appInstanceState" in deployment_response_data:
            originating_ad_objects = find_instance_details(federation_context_id, app_id, app_instance_id, zone_id)
            if originating_ad_objects:
                # Delete application deployment at partner
                response_data = fm_client.remove_app_deployment(originating_op_instance.partner_federation_id, app_id,
                                                                app_instance_id, zone_id, bearer_token,
                                                                partner_api_root)
                
                # Check if the response contains an error from the partner API
                if "error" in response_data and "status_code" in response_data:
                    status_code = response_data["status_code"]
                    error_message = response_data["error"]
                    raise APIError(status_code, f"Partner API error: {error_message}")
                elif "termination" in response_data:
                    # If application deployment has been removed well, remove application deployment from originating
                    originating_ad_objects.delete()
                    return response_data, 200
                else:
                    raise APIError(422, f"Unexpected response from partner API: {response_data}")
            else:
                raise APIError(409, "Application Deployment exist at partner operator but not in originating operator")
        else:
            if find_instance_details(federation_context_id, app_id, app_instance_id, zone_id):
                raise APIError(409, "Application Deployment exist in originating operator but not at partner operator")
            else:
                raise APIError(404, "Application Deployment not found")
    except ValidationError:
        raise APIError(400, f"Invalid federation context ID format: {federation_context_id}")
    except APIError:
        raise  # Re-raise APIError as-is
    except Exception as error:
        raise APIError(500, f"Error removing application. Reason: {error}")


def install_app_originating_op(federation_id, partner_federation_id, body, response_partner):
    # Convert the original model instance to the MongoEngine document
    deployment_data = {
        "orig_ad_federation_context_id": federation_id,
        "orig_ad_instance_id": response_partner.get("appInstIdentifier"),
        "orig_ad_app_id": body.app_id,
        "orig_ad_app_version": body.app_version,
        "orig_ad_app_provider_id": body.app_provider_id,
        "orig_ad_zone_info_zone_id": response_partner.get("zoneId"),
        "orig_ad_zone_info_flavour_id": body.zone_info.flavour_id,
        "orig_ad_zone_info_resource_consumption": body.zone_info.resource_consumption,
        "orig_ad_zone_info_res_pool": body.zone_info.res_pool,
        "orig_ad_app_inst_callback_link": body.app_inst_callback_link,
        "partner_federation_id": partner_federation_id
    }
    # Create a new MongoEngine document and save it to MongoDB
    new_deployment = OriginatingApplicationDeploymentManagementOriginatingOP(**deployment_data)
    new_deployment.save()


def find_all_instances_originating_op(federation_id, app_id, app_provider_id):
    originating_ad_objects = OriginatingApplicationDeploymentManagementOriginatingOP.objects(
        orig_ad_federation_context_id=federation_id,
        orig_ad_app_id=app_id,
        orig_ad_app_provider_id=app_provider_id
    )
    return originating_ad_objects


def find_instance_details(federation_id, app_id, instance_id, zone_id):
    originating_ad_objects = OriginatingApplicationDeploymentManagementOriginatingOP.objects(
        orig_ad_federation_context_id=federation_id,
        orig_ad_app_id=app_id,
        orig_ad_instance_id=instance_id,
        orig_ad_zone_info_zone_id=zone_id
    )
    return originating_ad_objects
+516 −0

File added.

Preview size limit exceeded, changes collapsed.

+143 −0
Original line number Diff line number Diff line
# -------------------------------------------------------------------------- #
# Copyright 2025-present, Federation Manager, by Software Networks, i2CAT    #
#                                                                            #
# 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 flask import abort

from adapters.error import APIError
from adapters.injector import resolve_adapter
from models.application_lcm_body import ApplicationLcmBody  # noqa: E501
import connexion
import util


def get_all_app_instances(federation_context_id, app_id, app_provider_id):  # noqa: E501
    """Retrieves all application instance of partner OP

     # noqa: E501

    :param federation_context_id:
    :type federation_context_id: dict | bytes
    :param app_id:
    :type app_id: dict | bytes
    :param app_provider_id:
    :type app_provider_id: dict | bytes

    :rtype: List[InlineResponse2009]
    """

    # Extract the token and the headers
    bearer_token = util.get_token_from_request(connexion)
    headers = dict(connexion.request.headers)
    partner_api_root = headers.get("X-Partner-Api-Root")

    try:
        adapter = resolve_adapter(headers)
        return adapter.application_deployment_management.get_all_app_instances(federation_context_id, app_id,
                                                                               app_provider_id, bearer_token,
                                                                               partner_api_root)
    except APIError as error:
        abort(error.status_code, error.detail_error)


def get_app_instance_details(federation_context_id, app_id, app_instance_id, zone_id):  # noqa: E501
    """Retrieves an application instance details from partner OP.

     # noqa: E501

    :param federation_context_id:
    :type federation_context_id: dict | bytes
    :param app_id:
    :type app_id: dict | bytes
    :param app_instance_id:
    :type app_instance_id: dict | bytes
    :param zone_id:
    :type zone_id: dict | bytes

    :rtype: InlineResponse2008
    """

    # Extract the token and the headers
    bearer_token = util.get_token_from_request(connexion)
    headers = dict(connexion.request.headers)
    partner_api_root = headers.get("X-Partner-Api-Root")

    try:
        adapter = resolve_adapter(headers)
        return adapter.application_deployment_management.get_app_instance_details(federation_context_id, app_id,
                                                                                  app_instance_id, zone_id,
                                                                                  bearer_token, partner_api_root)
    except APIError as error:
        abort(error.status_code, error.detail_error)


def install_app(federation_context_id, body=None):  # noqa: E501
    """Instantiates an application on a partner OP zone.

     # noqa: E501

    :param federation_context_id:
    :type federation_context_id: dict | bytes
    :param body: Details about application and zones where application instance should be created. It also definea call back URI which the partner OP shall use update home OP about a change in instance status.
    :type body: dict | bytes

    :rtype: InlineResponse202
    """

    # Extract the token and the headers
    bearer_token = util.get_token_from_request(connexion)
    headers = dict(connexion.request.headers)
    partner_api_root = headers.get("X-Partner-Api-Root")

    try:
        body = ApplicationLcmBody.from_dict(body)  # noqa: E501
    except Exception as error:
        raise APIError(422, f" Error: {error}")

    try:
        adapter = resolve_adapter(headers)
        return adapter.application_deployment_management.install_app(federation_context_id, body, bearer_token,
                                                                     partner_api_root)
    except APIError as error:
        abort(error.status_code, error.detail_error)


def remove_app(federation_context_id, app_id, app_instance_id, zone_id):  # noqa: E501
    """Terminate an application instance on a partner OP zone.

     # noqa: E501

    :param federation_context_id:
    :type federation_context_id: dict | bytes
    :param app_id:
    :type app_id: dict | bytes
    :param app_instance_id:
    :type app_instance_id: dict | bytes
    :param zone_id:
    :type zone_id: dict | bytes

    :rtype: None
    """

    # Extract the token and the headers
    bearer_token = util.get_token_from_request(connexion)
    headers = dict(connexion.request.headers)
    partner_api_root = headers.get("X-Partner-Api-Root")

    try:
        adapter = resolve_adapter(headers)
        return adapter.application_deployment_management.remove_app(federation_context_id, app_id, app_instance_id,
                                                                    zone_id, bearer_token, partner_api_root)
    except APIError as error:
        abort(error.status_code, error.detail_error)
+0 −726

File deleted.

Preview size limit exceeded, changes collapsed.