Commit 04ca70a0 authored by Karagkounis Dimitris's avatar Karagkounis Dimitris
Browse files

write test for submit app controller. fix bugs. update app controllers models

parent 85d6d403
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
from flask import jsonify

from pydantic import ValidationError

# from edge_cloud_management_api.models.application_models import AppManifest
from edge_cloud_management_api.managers.log_manager import logger
from edge_cloud_management_api.models.application_models import AppManifest


def submit_app(body: dict):
@@ -11,7 +11,7 @@ def submit_app(body: dict):
    """
    try:
        # Validate the input data using Pydantic
        # validated_data = AppManifest(**body)
        validated_data = AppManifest(**body)

        # Insert into MongoDB
        # app_id = mongo.db.applications.insert_one(validated_data.dict()).inserted_id
@@ -96,7 +96,8 @@ def create_app_instance(body, app_id, x_correlator=None): # noqa: E501
    """
    try:
        return {}, 202  # application instantiation accepted
    except:
    except Exception:
        logger.exception("Error while creating app instance")
        error = {
            "status": 500,
            "code": "INTERNAL",
+13 −9
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ def get_all_cloud_zones() -> List[EdgeCloudZone]:
    return get_local_zones() + get_federated_zones()


def get_edge_cloud_zones(x_correlator=None, region=None, status=None):  # noqa: E501
def get_edge_cloud_zones(x_correlator: str | None = None, region=None, status=None):  # noqa: E501
    """Retrieve a list of the operators Edge Cloud Zones and their status

    List of the operators Edge Cloud Zones and their status, ordering the results by location and filtering by status (active/inactive/unknown)  # noqa: E501
@@ -48,11 +48,11 @@ def get_edge_cloud_zones(x_correlator=None, region=None, status=None): # noqa:
    :param x_correlator: Correlation id for the different services
    :type x_correlator: str
    :param region: Human readable name of the geographical Edge Cloud Region of the Edge Cloud. Defined by the Edge Cloud Provider.
    :type region: dict | bytes
    :type region: str
    :param status: Human readable status of the Edge Cloud Zone
    :type status: dict | bytes
    :type status: str

    :rtype: EdgeCloudZones
    :rtype: list[EdgeCloudZone]
    """
    try:
        query_params = EdgeCloudQueryParams(
@@ -61,11 +61,15 @@ def get_edge_cloud_zones(x_correlator=None, region=None, status=None): # noqa:
            status=status,
        )

        filtered_zones = [
            zone
            for zone in get_all_cloud_zones()
            if ((query_params.region is None) or (zone["edgeCloudRegion"] == query_params.region)) and ((query_params.status is None) or (zone["edgeCloudZoneStatus"] == query_params.status))
        ]
        def query_region_matches(zone: str) -> bool:
            """If region is None, return True (don't apply region filtering), otherwise check if the zone region matches the query region"""
            return query_params.region is None or zone["edgeCloudRegion"] == query_params.region

        def query_status_matches(zone: str) -> bool:
            """If status is None, return True (don't apply status filtering), otherwise check if the zone status matches the query status"""
            return (query_params.status is None) or (zone["edgeCloudZoneStatus"] == query_params.status)

        filtered_zones = [zone for zone in get_all_cloud_zones() if (query_region_matches(zone) and query_status_matches(zone))]
        response = [EdgeCloudZone(**zone).model_dump() for zone in filtered_zones]
        return jsonify(response), 200

+70 −263
Original line number Diff line number Diff line
@@ -3,72 +3,68 @@ from typing import List, Optional
from ipaddress import IPv4Address, IPv6Address
from enum import Enum

from edge_cloud_management_api.models.edge_cloud_models import EdgeCloudZone
# from edge_cloud_management_api.models.edge_cloud_models import EdgeCloudZone


# Enum definitions
class PackageType(str, Enum):
    QCOW2 = "QCOW2"
    OVA = "OVA"
    CONTAINER = "CONTAINER"
    HELM = "HELM"


class VisibilityType(str, Enum):
    VISIBILITY_EXTERNAL = "VISIBILITY_EXTERNAL"
    VISIBILITY_INTERNAL = "VISIBILITY_INTERNAL"
# class VisibilityType(str, Enum):
#     VISIBILITY_EXTERNAL = "VISIBILITY_EXTERNAL"
#     VISIBILITY_INTERNAL = "VISIBILITY_INTERNAL"


class AppInstanceStatus(str, Enum):
    ready = "ready"
    instantiating = "instantiating"
    failed = "failed"
    terminating = "terminating"
    unknown = "unknown"
# class AppInstanceStatus(str, Enum):
#     ready = "ready"
#     instantiating = "instantiating"
#     failed = "failed"
#     terminating = "terminating"
#     unknown = "unknown"


class Protocol(str, Enum):
    TCP = "TCP"
    UDP = "UDP"
    ANY = "ANY"
# class Protocol(str, Enum):
#     TCP = "TCP"
#     UDP = "UDP"
#     ANY = "ANY"


# Model definitions
class NetworkInterface(BaseModel):
    interfaceId: str = Field(..., regex=r"^[A-Za-z][A-Za-z0-9_]{3,31}$")
    protocol: str  # [ TCP, UDP, ANY ]
    port: int  # minimum: 1, maximum: 65535
    visibilityType: str  # [ VISIBILITY_EXTERNAL, VISIBILITY_INTERNAL ]

# class AccessEndpoint(BaseModel):
#     port: int  # min 0
#     fqdn: Optional[str]
#     ipv4Addresses: Optional[List[IPv4Address]]  # minItems: 1
#     ipv6Addresses: Optional[List[IPv6Address]]  # minItems: 1

class AccessEndpoint(BaseModel):
    port: int  # min 0
    fqdn: Optional[str]
    ipv4Addresses: Optional[List[IPv4Address]]  # minItems: 1
    ipv6Addresses: Optional[List[IPv6Address]]  # minItems: 1
#     class Config:
#         schema_extra = {
#             "example": {
#                 "port": 8080,
#                 "fqdn": "example.com",
#                 "ipv4Addresses": ["192.168.0.1"],
#                 "ipv6Addresses": ["2001:db8::1"],
#             }
#         }

    class Config:
        schema_extra = {
            "example": {
                "port": 8080,
                "fqdn": "example.com",
                "ipv4Addresses": ["192.168.0.1"],
                "ipv6Addresses": ["2001:db8::1"],
            }
        }

# class ComponentEndpointInfo(BaseModel):
#     interfaceId: UUID4  # string pattern: ^[A-Za-z0-9][A-Za-z0-9_]{6,30}[A-Za-z0-9]$
#     accessPoints: AccessEndpoint

class ComponentEndpointInfo(BaseModel):
    interfaceId: UUID4  # string pattern: ^[A-Za-z0-9][A-Za-z0-9_]{6,30}[A-Za-z0-9]$
    accessPoints: AccessEndpoint

# class AppInstanceInfo(BaseModel):
#     appInstanceId: UUID4  # str = Field(..., regex=r"^[0-9a-fA-F-]{36}$")
#     status: AppInstanceStatus = AppInstanceStatus.unknown  # [ ready, instantiating, failed, terminating, unknown ]
#     componentEndpointInfo: List[ComponentEndpointInfo]
#     kubernetesClusterRef: Optional[UUID4]
#     edgeCloudZone: EdgeCloudZone

class AppInstanceInfo(BaseModel):
    appInstanceId: UUID4  # str = Field(..., regex=r"^[0-9a-fA-F-]{36}$")
    status: AppInstanceStatus = AppInstanceStatus.unknown  # [ ready, instantiating, failed, terminating, unknown ]
    componentEndpointInfo: List[ComponentEndpointInfo]
    kubernetesClusterRef: Optional[UUID4]
    edgeCloudZone: EdgeCloudZone

class NetworkInterface(BaseModel):
    interfaceId: str = Field(..., pattern="^[A-Za-z][A-Za-z0-9_]{3,31}$")
    protocol: str  # [ TCP, UDP, ANY ]
    port: int  # minimum: 1, maximum: 65535
    visibilityType: str  # [ VISIBILITY_EXTERNAL, VISIBILITY_INTERNAL ]


class ComponentSpec(BaseModel):
@@ -76,14 +72,13 @@ class ComponentSpec(BaseModel):
    networkInterfaces: List[NetworkInterface]  # min one occurrence


class AppRepo(BaseModel):
    class AppRepoAuthType(str, Enum):
        DOCKER = "DOCKER"
        HTTP_BASIC = "HTTP_BASIC"
        HTTP_BEARER = "HTTP_BEARER"
        NONE = "NONE"


class AppRepo(BaseModel):
    type: str  # [ PRIVATEREPO, PUBLICREPO ]
    imagePath: HttpUrl
    userName: Optional[str]
@@ -92,11 +87,12 @@ class AppRepo(BaseModel):
    checksum: Optional[str]  # MD5 checksum for VM and file-based images, sha256 digest for containers


class RequiredResources(BaseModel):
    numCPU: int
    memory: int
    storage: int

class AppManifest(BaseModel):
    class PackageType(str, Enum):
        QCOW2 = "QCOW2"
        OVA = "OVA"
        CONTAINER = "CONTAINER"
        HELM = "HELM"

    class OperatingSystem(BaseModel):
        architecture: str  # [ x86_64, x86 ]
@@ -104,11 +100,14 @@ class OperatingSystem(BaseModel):
        version: str  # Version of the OS # [ OS_VERSION_UBUNTU_2204_LTS, OS_VERSION_RHEL_8, OS_MS_WINDOWS_2022, OTHER ]
        license: str  # License needed to activate the OS # [ OS_LICENSE_TYPE_FREE, OS_LICENSE_TYPE_ON_DEMAND, OTHER ]

    class RequiredResources(BaseModel):
        numCPU: int
        memory: int
        storage: int

class AppManifest(BaseModel):
    appId: Optional[UUID4]
    name: str = Field(..., regex=r"^[A-Za-z][A-Za-z0-9_]{1,63}$")
    appProvider: str = Field(..., regex=r"^[A-Za-z][A-Za-z0-9_]{7,63}$")
    name: str = Field(..., pattern="^[A-Za-z][A-Za-z0-9_]{1,63}$")
    appProvider: str = Field(..., pattern="^[A-Za-z][A-Za-z0-9_]{7,63}$")
    version: str  # application version
    packageType: PackageType
    operatingSystem: Optional[OperatingSystem]
@@ -117,203 +116,11 @@ class AppManifest(BaseModel):
    componentSpec: List[ComponentSpec]


class AppZones(BaseModel):
    kubernetesClusterRef: Optional[UUID4]
    EdgeCloudZone: EdgeCloudZone


class AppInstance(BaseModel):
    appId: UUID4
    appZones: List[AppZones]


###########################
###########################
###########################
###########################
###########################
###########################
###########################
###########################
###########################
from pydantic import UUID4, BaseModel, HttpUrl, Field, StringConstraints
from typing import Annotated, List, Optional
from ipaddress import IPv4Address, IPv6Address
from enum import Enum

from edge_cloud_management_api.models.edge_cloud_models import EdgeCloudZone


class OperatingSystem(BaseModel):
    class OSArchitecture(str, Enum):
        x86_64 = "x86_64"
        x86 = "x86"

    class OSFamily(str, Enum):
        RHEL = "RHEL"
        UBUNTU = "UBUNTU"
        COREOS = "COREOS"
        WINDOWS = "WINDOWS"
        OTHER = "OTHER"

    class OSVersion(str, Enum):
        OS_VERSION_UBUNTU_2204_LTS = "OS_VERSION_UBUNTU_2204_LTS"
        OS_VERSION_RHEL_8 = "OS_VERSION_RHEL_8"
        OS_MS_WINDOWS_2022 = "OS_MS_WINDOWS_2022"
        OTHER = "OTHER"

    class OSLicense(str, Enum):
        OS_LICENSE_TYPE_FREE = "OS_LICENSE_TYPE_FREE"
        OS_LICENSE_TYPE_ON_DEMAND = "OS_LICENSE_TYPE_ON_DEMAND"
        OTHER = "OTHER"

    architecture: OSArchitecture
    family: OSFamily
    version: OSVersion
    license: OSLicense
# class AppZones(BaseModel):
#     kubernetesClusterRef: Optional[UUID4]
#     EdgeCloudZone: EdgeCloudZone


class AppRepo(BaseModel):
    class AppRepoType(str, Enum):
        PRIVATEREPO = "PRIVATEREPO"
        PUBLICREPO = "PUBLICREPO"

    class AppRepoAuthType(str, Enum):
        DOCKER = "DOCKER"
        HTTP_BASIC = "HTTP_BASIC"
        HTTP_BEARER = "HTTP_BEARER"
        NONE = "NONE"

    type: AppRepoType
    imagePath: HttpUrl
    userName: Optional[str]
    credentials: Optional[Annotated[str, StringConstraints(max_length=128)]]
    authType: Optional[AppRepoAuthType]
    checksum: Optional[str]
    # MD5 checksum for VM and file-based images, sha256 digest for containers


from pydantic import BaseModel, Field
from typing import Union, Optional, List


class K8sNetworking(BaseModel):
    networkPolicy: Optional[str]
    serviceMesh: Optional[str]


class K8sAddons(BaseModel):
    monitoring: bool
    logging: bool
    autoscaling: bool


class GpuInfo(BaseModel):
    gpuCount: int
    gpuType: str


class AdditionalStorage(BaseModel):
    size: int  # in GB
    storageClass: Optional[str]


class RequiredResources(BaseModel):
    class KubernetesResources(BaseModel):
        infraKind: str = Field(..., description="Type of infrastructure: Kubernetes")
        applicationResources: dict
        isStandalone: bool
        version: Optional[str]
        additionalStorage: Optional[List[AdditionalStorage]]
        networking: Optional[K8sNetworking]
        addons: Optional[K8sAddons]

    class VmResources(BaseModel):
        infraKind: str = Field(..., description="Type of infrastructure: Virtual Machine")
        numCPU: int
        memory: int  # in MB
        additionalStorages: Optional[List[AdditionalStorage]]
        gpu: Optional[GpuInfo]

    class ContainerResources(BaseModel):
        infraKind: str = Field(..., description="Type of infrastructure: Container")
        numCPU: int
        memory: int  # in MB
        storage: Optional[AdditionalStorage]
        gpu: Optional[GpuInfo]

    class DockerComposeResources(BaseModel):
        infraKind: str = Field(..., description="Type of infrastructure: Docker Compose")
        numCPU: int
        memory: int  # in MB
        storage: Optional[AdditionalStorage]
        gpu: Optional[GpuInfo]

    __root__: Union[KubernetesResources, VmResources, ContainerResources, DockerComposeResources]


# class ComponentSpec(BaseModel):
#     class NetworkInterface(BaseModel):
#         interfaceId: str = Field(..., regex=r"^[A-Za-z][A-Za-z0-9_]{3,31}$")
#         protocol: str  # [ TCP, UDP, ANY ]
#         port: int  # minimum: 1, maximum: 65535
#         visibilityType: str  # [ VISIBILITY_EXTERNAL, VISIBILITY_INTERNAL ]

#     componentName: str
#     networkInterfaces: List[NetworkInterface]  # min one occurrence


from pydantic import BaseModel, Field
from typing import List, Optional
from enum import Enum


# Enums
class Protocol(str, Enum):
    TCP = "TCP"
    UDP = "UDP"
    ANY = "ANY"


class VisibilityType(str, Enum):
    VISIBILITY_EXTERNAL = "VISIBILITY_EXTERNAL"
    VISIBILITY_INTERNAL = "VISIBILITY_INTERNAL"


# Models
class NetworkInterface(BaseModel):
    interfaceId: str = Field(
        ...,
        regex=r"^[A-Za-z][A-Za-z0-9_]{3,31}$",
        description="Unique identifier for the network interface.",
    )
    protocol: Protocol = Field(..., description="IP transport communication protocol.")
    port: int = Field(..., ge=1, le=65535, description="Port number exposed by the component.")
    visibilityType: VisibilityType = Field(..., description="Defines whether the interface is external or internal.")


class ComponentSpec(BaseModel):
    componentName: str = Field(..., description="Unique name for the component within the application.")
    networkInterfaces: List[NetworkInterface] = Field(
        ...,
        min_items=1,
        description="List of network interfaces exposed by the component.",
    )


class AppManifest(BaseModel):
    class PackageType(str, Enum):
        QCOW2 = "QCOW2"
        OVA = "OVA"
        CONTAINER = "CONTAINER"
        HELM = "HELM"

    appId: Optional[UUID4]  # if not provided the backend has to create it
    name: str = Field(..., regex=r"^[A-Za-z][A-Za-z0-9_]{1,63}$")
    appProvider: str = Field(..., regex=r"^[A-Za-z][A-Za-z0-9_]{7,63}$")
    version: str  # application version
    packageType: PackageType
    operatingSystem: Optional[OperatingSystem]
    appRepo: AppRepo
    requiredResources: RequiredResources
    componentSpec: List[ComponentSpec]
# class AppInstance(BaseModel):
#     appId: UUID4
#     appZones: List[AppZones]
+7 −12
Original line number Diff line number Diff line
import requests
from edge_cloud_management_api.configs.logger_config import logger
from edge_cloud_management_api.managers.log_manager import logger
from requests.exceptions import Timeout, ConnectionError
from edge_cloud_management_api.configs.env_config import (
    PI_EDGE_BASE_URL,
    PI_EDGE_PASSWORD,
    PI_EDGE_USERNAME,
    HTTP_PROXY,
)
from edge_cloud_management_api.configs.env_config import Config


proxies = {
    "http": HTTP_PROXY,
    "https": HTTP_PROXY,
    "http": Config.HTTP_PROXY,
    "https": Config.HTTP_PROXY,
}


@@ -160,9 +155,9 @@ class PiEdgeAPIClientFactory:
    """

    def __init__(self):
        self.default_base_url = PI_EDGE_BASE_URL
        self.default_username = PI_EDGE_USERNAME
        self.default_password = PI_EDGE_PASSWORD
        self.default_base_url = Config.PI_EDGE_BASE_URL
        self.default_username = Config.PI_EDGE_USERNAME
        self.default_password = Config.PI_EDGE_PASSWORD

    def create_pi_edge_api_client(self, base_url=None, username=None, password=None):
        """
+77 −0
Original line number Diff line number Diff line
{
    "appId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "name": "H38L1jMMndtyRR3WFCSI_he_m4NwQ0rggZYrOX9r09shL597SF4bGHoqUI5t",
    "appProvider": "rQxQr3mSdo3dgYZTh1Hq55CWIqsEPHqB80P9_ja3HH8Bp9Hj9Ygc",
    "version": "string",
    "packageType": "QCOW2",
    "operatingSystem": {
        "architecture": "x86_64",
        "family": "RHEL",
        "version": "OS_VERSION_UBUNTU_2204_LTS",
        "license": "OS_LICENSE_TYPE_FREE"
    },
    "appRepo": {
        "type": "PRIVATEREPO",
        "imagePath": "https://charts.bitnami.com/bitnami/helm/example-chart:0.1.0",
        "userName": "string",
        "credentials": "string",
        "authType": "DOCKER",
        "checksum": "string"
    },
    "requiredResources": {
        "applicationResources": {
            "cpuPool": {
                "numCPU": 1,
                "memory": 1024,
                "topology": {
                    "minNumberOfNodes": 5,
                    "minNodeCpu": 2,
                    "minNodeMemory": 1024
                }
            },
            "gpuPool": {
                "numCPU": 1,
                "memory": 1024,
                "gpuMemory": 16,
                "topology": {
                    "minNumberOfNodes": 2,
                    "minNodeCpu": 2,
                    "minNodeMemory": 1024,
                    "minNodeGpuMemory": 8
                }
            }
        },
        "isStandalone": false,
        "version": "1.29",
        "additionalStorage": "80GB",
        "networking": {
            "primaryNetwork": {
                "provider": "cilium",
                "version": "1.13"
            },
            "additionalNetworks": [
                {
                    "name": "net1",
                    "interfaceType": "vfio-pci"
                }
            ]
        },
        "addons": {
            "monitoring": true,
            "ingress": true
        }
    },
    "componentSpec": [
        {
            "componentName": "string",
            "networkInterfaces": [
                {
                    "interfaceId": "I4Xp",
                    "protocol": "TCP",
                    "port": 65535,
                    "visibilityType": "VISIBILITY_EXTERNAL"
                }
            ]
        }
    ]
}
 No newline at end of file
Loading