Commit 451ffecb authored by George Papathanail's avatar George Papathanail
Browse files

update: code transfer

parent 064ace33
Loading
Loading
Loading
Loading
Loading
+0 −0

Empty file deleted.

+0 −111
Original line number Diff line number Diff line
from typing import Optional
from uuid import UUID

from sqlalchemy import Column, MetaData, String, Table, delete, select
from sqlalchemy.dialects.postgresql import JSONB, insert
from sqlalchemy.dialects.postgresql import UUID as PG_UUID
from sqlalchemy.ext.asyncio import AsyncEngine

from open_exposure_gateway.app.api.camara.edge_application_management.ceee3a6.schemas import (
    AppInstanceInfo,
    AppManifest,
)

metadata = MetaData()

apps_table = Table(
    "apps",
    metadata,
    Column("app_id", PG_UUID(as_uuid=True), primary_key=True),
    Column("data", JSONB, nullable=False),
)

app_instances_table = Table(
    "app_instances",
    metadata,
    Column("app_instance_id", PG_UUID(as_uuid=True), primary_key=True),
    Column("app_id", PG_UUID(as_uuid=True), nullable=False, index=True),
    Column("edge_cloud_region", String, nullable=True, index=True),
    Column("data", JSONB, nullable=False),
)


class PostgresStorageAdapter:
    def __init__(self, engine: AsyncEngine) -> None:
        self._engine = engine

    async def create_tables(self) -> None:
        async with self._engine.begin() as conn:
            await conn.run_sync(metadata.create_all)

    async def store_app(self, app: AppManifest) -> None:
        async with self._engine.begin() as conn:
            stmt = (
                insert(apps_table)
                .values(app_id=app.appId, data=app.model_dump(mode="json"))
                .on_conflict_do_update(
                    index_elements=["app_id"],
                    set_={"data": app.model_dump(mode="json")},
                )
            )
            await conn.execute(stmt)

    async def get_app(self, app_id: UUID) -> Optional[AppManifest]:
        async with self._engine.connect() as conn:
            result = await conn.execute(select(apps_table).where(apps_table.c.app_id == app_id))
            row = result.fetchone()
            if row is None:
                return None
            return AppManifest.model_validate(row.data)

    async def get_apps(self) -> list[AppManifest]:
        async with self._engine.connect() as conn:
            result = await conn.execute(select(apps_table))
            return [AppManifest.model_validate(row.data) for row in result]

    async def delete_app(self, app_id: UUID) -> None:
        async with self._engine.begin() as conn:
            await conn.execute(delete(apps_table).where(apps_table.c.app_id == app_id))

    async def store_app_instance(self, instance: AppInstanceInfo) -> None:
        region = instance.edgeCloudZone.edgeCloudRegion if instance.edgeCloudZone else None
        async with self._engine.begin() as conn:
            stmt = (
                insert(app_instances_table)
                .values(
                    app_instance_id=instance.appInstanceId,
                    app_id=instance.edgeCloudZone.edgeCloudZoneId,
                    edge_cloud_region=region,
                    data=instance.model_dump(mode="json"),
                )
                .on_conflict_do_update(
                    index_elements=["app_instance_id"],
                    set_={"data": instance.model_dump(mode="json")},
                )
            )
            await conn.execute(stmt)

    async def get_app_instances(
        self,
        app_id: Optional[UUID] = None,
        app_instance_id: Optional[UUID] = None,
        region: Optional[str] = None,
    ) -> list[AppInstanceInfo]:
        stmt = select(app_instances_table)
        if app_id is not None:
            stmt = stmt.where(app_instances_table.c.app_id == app_id)
        if app_instance_id is not None:
            stmt = stmt.where(app_instances_table.c.app_instance_id == app_instance_id)
        if region is not None:
            stmt = stmt.where(app_instances_table.c.edge_cloud_region == region)
        async with self._engine.connect() as conn:
            result = await conn.execute(stmt)
            return [AppInstanceInfo.model_validate(row.data) for row in result]

    async def delete_app_instance(self, app_instance_id: UUID) -> None:
        async with self._engine.begin() as conn:
            await conn.execute(
                delete(app_instances_table).where(
                    app_instances_table.c.app_instance_id == app_instance_id
                )
            )
+0 −149
Original line number Diff line number Diff line
from typing import Annotated, Any, Optional
from uuid import UUID

from fastapi import APIRouter, Depends, Header, Query, Response, status

from open_exposure_gateway.app.api.camara.edge_application_management.ceee3a6.schemas import (
    AppInstanceListResponse,
    AppInstancesCreatedResponse,
    AppManifest,
    AppManifestEnvelope,
    CreateAppInstanceRequest,
    EdgeCloudZone,
    SubmittedApp,
)
from open_exposure_gateway.app.application.services.edge_application_management_service import (
    EdgeApplicationManagementService,
)
from open_exposure_gateway.app.dependencies import get_edge_app_service

router = APIRouter()

EdgeAppService = Annotated[EdgeApplicationManagementService, Depends(get_edge_app_service)]


@router.get(
    "/edge-cloud-zones",
    tags=["Edge Cloud"],
    summary="Retrieve a list of the operators Edge Cloud Zones and their status",
    response_model=list[EdgeCloudZone],
)
async def get_edge_cloud_zones(
    service: EdgeAppService,
    region: Annotated[Optional[str], Query()] = None,
    status: Annotated[Optional[str], Query()] = None,
    x_correlator: Annotated[Optional[str], Header(alias="x-correlator")] = None,
) -> Any:
    return await service.get_edge_cloud_zones(
        region=region,
        status=status,
        x_correlator=x_correlator,
    )


@router.get(
    "/apps",
    tags=["Application"],
    summary="Retrieve a list of existing Applications",
    response_model=list[AppManifest],
)
async def get_apps(
    service: EdgeAppService,
    x_correlator: Annotated[Optional[str], Header(alias="x-correlator")] = None,
) -> Any:
    return await service.get_apps(x_correlator=x_correlator)


@router.get(
    "/apps/{appId}",
    tags=["Application"],
    summary="Retrieve the information of an Application",
    response_model=AppManifestEnvelope,
)
async def get_app(
    appId: UUID,
    service: EdgeAppService,
    x_correlator: Annotated[Optional[str], Header(alias="x-correlator")] = None,
) -> Any:
    return await service.get_app(app_id=appId, x_correlator=x_correlator)


@router.post(
    "/apps",
    tags=["Application"],
    status_code=201,
    summary="Submit application metadata to the Edge Cloud Provider.",
    response_model=SubmittedApp,
)
async def submit_app(
    request: AppManifest,
    service: EdgeAppService,
    x_correlator: Annotated[Optional[str], Header(alias="x-correlator")] = None,
) -> Any:
    return await service.submit_app(request=request, x_correlator=x_correlator)


@router.delete(
    "/apps/{appId}",
    tags=["Application"],
    status_code=202,
    summary="Delete an Application from an Edge Cloud Provider",
)
async def delete_app(
    appId: UUID,
    service: EdgeAppService,
    x_correlator: Annotated[Optional[str], Header(alias="x-correlator")] = None,
) -> Response:
    await service.delete_app(app_id=appId, x_correlator=x_correlator)
    return Response(status_code=status.HTTP_202_ACCEPTED)


@router.post(
    "/appinstances",
    tags=["Application"],
    status_code=202,
    summary="Instantiation of an Application",
    response_model=AppInstancesCreatedResponse,
)
async def create_app_instance(
    request: CreateAppInstanceRequest,
    service: EdgeAppService,
    x_correlator: Annotated[Optional[str], Header(alias="x-correlator")] = None,
) -> Any:
    return await service.create_app_instance(request=request, x_correlator=x_correlator)


@router.get(
    "/appinstances",
    tags=["Application"],
    summary="Retrieve the information of Application Instances for a given App",
    response_model=AppInstanceListResponse,
)
async def get_app_instances(
    service: EdgeAppService,
    appId: Annotated[Optional[UUID], Query()] = None,
    appInstanceId: Annotated[Optional[UUID], Query()] = None,
    region: Annotated[Optional[str], Query()] = None,
    x_correlator: Annotated[Optional[str], Header(alias="x-correlator")] = None,
) -> Any:
    return await service.get_app_instances(
        app_id=appId,
        app_instance_id=appInstanceId,
        region=region,
        x_correlator=x_correlator,
    )


@router.delete(
    "/appinstances/{appInstanceId}",
    tags=["Application"],
    status_code=202,
    summary="Terminate an Application Instance",
)
async def delete_app_instance(
    appInstanceId: UUID,
    service: EdgeAppService,
    x_correlator: Annotated[Optional[str], Header(alias="x-correlator")] = None,
) -> Response:
    await service.delete_app_instance(app_instance_id=appInstanceId, x_correlator=x_correlator)
    return Response(status_code=status.HTTP_202_ACCEPTED)
+0 −154
Original line number Diff line number Diff line
from enum import StrEnum
from typing import Any, Literal, Optional
from uuid import UUID

from pydantic import BaseModel, Field


class AppInstanceStatus(StrEnum):
    READY = "ready"
    INSTANTIATING = "instantiating"
    FAILED = "failed"
    TERMINATING = "terminating"
    UNKNOWN = "unknown"


class EdgeCloudZoneStatus(StrEnum):
    ACTIVE = "active"
    INACTIVE = "inactive"
    UNKNOWN = "unknown"


class EdgeCloudZone(BaseModel):
    edgeCloudZoneId: UUID
    edgeCloudZoneName: str = Field(max_length=64)
    edgeCloudProvider: str = Field(max_length=64)
    edgeCloudZoneStatus: EdgeCloudZoneStatus = EdgeCloudZoneStatus.UNKNOWN
    edgeCloudRegion: Optional[str] = Field(default=None, max_length=64)


class SubmittedApp(BaseModel):
    appId: UUID


class AppRepo(BaseModel):
    type: Literal["PRIVATEREPO", "PUBLICREPO"]
    imagePath: str = Field(max_length=2048)
    userName: Optional[str] = Field(default=None, max_length=64)
    credentials: Optional[str] = Field(default=None, max_length=2048)
    authType: Optional[Literal["DOCKER", "HTTP_BASIC", "HTTP_BEARER", "NONE"]] = None
    checksum: Optional[str] = Field(default=None, max_length=128)


class OperatingSystem(BaseModel):
    architecture: Literal["x86_64", "x86"]
    family: Literal["RHEL", "UBUNTU", "COREOS", "WINDOWS", "OTHER"]
    version: Literal[
        "OS_VERSION_UBUNTU_2204_LTS",
        "OS_VERSION_RHEL_8",
        "OS_MS_WINDOWS_2022",
        "OTHER",
    ]
    license: Literal["OS_LICENSE_TYPE_FREE", "OS_LICENSE_TYPE_ON_DEMAND", "OTHER"]


class NetworkInterface(BaseModel):
    interfaceId: str = Field(
        min_length=4,
        max_length=32,
        pattern=r"^[A-Za-z0-9][A-Za-z0-9_]{2,30}[A-Za-z0-9]$",
    )
    protocol: Literal["TCP", "UDP", "ANY"]
    port: int = Field(ge=1, le=65535)
    visibilityType: Literal["VISIBILITY_EXTERNAL", "VISIBILITY_INTERNAL"]


class ComponentSpecItem(BaseModel):
    componentName: str = Field(max_length=64)
    networkInterfaces: list[NetworkInterface]


class VmResources(BaseModel):
    infraKind: Literal["virtualMachine"]
    numCPU: int = Field(ge=1, le=256)
    memory: int = Field(ge=1, le=32768)


class ContainerResources(BaseModel):
    infraKind: Literal["container"]
    numCPU: str = Field(pattern=r"^\d+((\.\d{1,3})|(m))?$")
    memory: int = Field(ge=1, le=16384)


class DockerComposeResources(BaseModel):
    infraKind: Literal["dockerCompose"]
    numCPU: int = Field(ge=1, le=256)
    memory: int = Field(ge=1, le=16384)


class KubernetesResources(BaseModel):
    infraKind: Literal["kubernetes"]
    applicationResources: dict[str, Any]
    isStandalone: bool


RequiredResources = VmResources | ContainerResources | DockerComposeResources | KubernetesResources


class AppManifest(BaseModel):
    appId: Optional[UUID] = None
    name: str = Field(max_length=64, pattern=r"^[A-Za-z][A-Za-z0-9_]{1,63}$")
    appProvider: Optional[str] = Field(
        default=None, max_length=64, pattern=r"^[A-Za-z][A-Za-z0-9_]{7,63}$"
    )
    version: str = Field(max_length=64)
    packageType: Literal["QCOW2", "OVA", "CONTAINER", "HELM", "CSAR"]
    operatingSystem: Optional[OperatingSystem] = None
    appRepo: AppRepo
    requiredResources: Optional[RequiredResources] = None
    componentSpec: list[ComponentSpecItem]


class AppManifestEnvelope(BaseModel):
    appManifest: AppManifest


class AccessEndpoint(BaseModel):
    port: int = Field(ge=1, le=65535)
    fqdn: Optional[str] = Field(default=None, max_length=253)
    ipv4Addresses: Optional[list[str]] = None
    ipv6Addresses: Optional[list[str]] = None


class ComponentEndpointInfo(BaseModel):
    interfaceId: str
    accessPoints: AccessEndpoint


class AppInstanceInfo(BaseModel):
    appInstanceId: UUID
    name: str
    appId: UUID
    appProvider: str
    status: AppInstanceStatus = AppInstanceStatus.UNKNOWN
    componentEndpointInfo: Optional[list[ComponentEndpointInfo]] = None
    kubernetesClusterRef: Optional[UUID] = None
    edgeCloudZone: EdgeCloudZone


class AppZone(BaseModel):
    kubernetesClusterRef: Optional[UUID] = None
    edgeCloudZone: EdgeCloudZone


class CreateAppInstanceRequest(BaseModel):
    appId: UUID
    appZones: list[AppZone]


class AppInstancesCreatedResponse(BaseModel):
    appInstances: list[AppInstanceInfo]


class AppInstanceListResponse(BaseModel):
    appInstanceInfo: list[AppInstanceInfo]