Loading edge_cloud_management_api/controllers/app_controllers.py +5 −4 Changes for edge_cloud_management_api/controllers/app_controllers.py: 5 added lines, 4 removed lines. 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): Loading @@ -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 Loading Loading @@ -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", Loading edge_cloud_management_api/controllers/edge_cloud_controller.py +13 −9 Changes for edge_cloud_management_api/controllers/edge_cloud_controller.py: 13 added lines, 9 removed lines. Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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( Loading @@ -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 Loading edge_cloud_management_api/models/application_models.py +62 −255 Changes for edge_cloud_management_api/models/application_models.py: 62 added lines, 255 removed lines. Original line number Diff line number Diff line Loading @@ -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): Loading @@ -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] Loading @@ -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 ] Loading @@ -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] Loading @@ -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] edge_cloud_management_api/services/pi_edge_services.py +7 −12 Changes for edge_cloud_management_api/services/pi_edge_services.py: 7 added lines, 12 removed lines. 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, } Loading Loading @@ -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): """ Loading tests/fixtures/submit_app.json 0 → 100644 +77 −0 Changes for tests/fixtures/submit_app.json: 77 added lines, 0 removed lines. 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
edge_cloud_management_api/controllers/app_controllers.py +5 −4 Changes for edge_cloud_management_api/controllers/app_controllers.py: 5 added lines, 4 removed lines. 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): Loading @@ -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 Loading Loading @@ -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", Loading
edge_cloud_management_api/controllers/edge_cloud_controller.py +13 −9 Changes for edge_cloud_management_api/controllers/edge_cloud_controller.py: 13 added lines, 9 removed lines. Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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( Loading @@ -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 Loading
edge_cloud_management_api/models/application_models.py +62 −255 Changes for edge_cloud_management_api/models/application_models.py: 62 added lines, 255 removed lines. Original line number Diff line number Diff line Loading @@ -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): Loading @@ -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] Loading @@ -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 ] Loading @@ -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] Loading @@ -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]
edge_cloud_management_api/services/pi_edge_services.py +7 −12 Changes for edge_cloud_management_api/services/pi_edge_services.py: 7 added lines, 12 removed lines. 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, } Loading Loading @@ -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): """ Loading
tests/fixtures/submit_app.json 0 → 100644 +77 −0 Changes for tests/fixtures/submit_app.json: 77 added lines, 0 removed lines. 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