From f7057df8c3537bacefcc5541f892581d121cd4f7 Mon Sep 17 00:00:00 2001
From: Laskaratos Dimitris <dlaskaratos@intracomtel.com>
Date: Wed, 28 May 2025 15:55:49 +0300
Subject: [PATCH] Fixed bugs related to edge cloud management api
---
.../{artifact/clients/skopeo => }/__init__.py | 0
.../edgecloud/clients/aeros/__init__.py | 21 ++
.../edgecloud/clients/aeros/client.py | 245 +++++++++++++++++-
.../edgecloud/clients/aeros/config.py | 24 ++
.../clients/aeros/continuum_client.py | 170 ++++++++++++
.../adapters/edgecloud/clients/aeros/utils.py | 43 +++
.../edgecloud/clients/i2edge/client.py | 194 +++++++++++++-
.../edgecloud/clients/i2edge/common.py | 100 +++++++
.../edgecloud/clients/i2edge/schemas.py | 167 ++++++++++++
.../edgecloud/clients/i2edge/utils.py | 151 +++++++++++
.../edgecloud/clients/piedge/client.py | 52 +++-
.../swagger_server/adapters/logger.py | 48 ++++
.../swagger_server/adapters/main.py | 49 ++++
.../skopeo/.gitkeep => network/__init__.py} | 0
.../.gitkeep => network/clients/__init__.py} | 0
.../adapters/network/clients/errors.py | 3 +
.../adapters/network/clients/oai/client.py | 30 +++
.../network/clients/open5gcore/__init__.py} | 0
.../network/clients/open5gcore/client.py | 30 +++
.../network/clients/open5gs/client.py | 67 +++++
.../network/clients/open5gs/common.py | 40 +++
.../network/clients/open5gs/schemas.py | 23 ++
.../adapters/network/core/__init__.py | 0
.../adapters/network/core/network_factory.py | 66 +++++
.../network/core/network_interface.py | 69 +++++
.../network_functions_controller.py | 58 ++++-
.../service_functions_instances_controller.py | 20 +-
.../swagger_server/swagger/swagger.yaml | 181 +++++++++++--
.../utils/kubernetes_connector.py | 4 +-
29 files changed, 1782 insertions(+), 73 deletions(-)
rename service-resource-manager-implementation/swagger_server/adapters/{artifact/clients/skopeo => }/__init__.py (100%)
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/config.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/continuum_client.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/utils.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/common.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/schemas.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/utils.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/logger.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/main.py
rename service-resource-manager-implementation/swagger_server/adapters/{artifact/clients/skopeo/.gitkeep => network/__init__.py} (100%)
rename service-resource-manager-implementation/swagger_server/adapters/{artifact/core/.gitkeep => network/clients/__init__.py} (100%)
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/network/clients/errors.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/network/clients/oai/client.py
rename service-resource-manager-implementation/swagger_server/{controllers/network_function_controller.py => adapters/network/clients/open5gcore/__init__.py} (100%)
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gcore/client.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/client.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/common.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/schemas.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/network/core/__init__.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/network/core/network_factory.py
create mode 100644 service-resource-manager-implementation/swagger_server/adapters/network/core/network_interface.py
diff --git a/service-resource-manager-implementation/swagger_server/adapters/artifact/clients/skopeo/__init__.py b/service-resource-manager-implementation/swagger_server/adapters/__init__.py
similarity index 100%
rename from service-resource-manager-implementation/swagger_server/adapters/artifact/clients/skopeo/__init__.py
rename to service-resource-manager-implementation/swagger_server/adapters/__init__.py
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/__init__.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/__init__.py
index e69de29..191fcb7 100644
--- a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/__init__.py
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/__init__.py
@@ -0,0 +1,21 @@
+"""
+aerOS client
+ This module provides a client for interacting with the aerOS REST API.
+ It includes methods for onboarding/deploying applications,
+ and querying aerOS continuum entities
+ aerOS domain is exposed as zones
+ aerOS services and service components are exposed as applications
+ Client is initialized with a base URL for the aerOS API
+ and an access token for authentication.
+"""
+
+from src.edgecloud.clients.aeros import config
+from src.logger import setup_logger
+
+logger = setup_logger(__name__, is_debug=True, file_name=config.LOG_FILE)
+
+logger.info("aerOS client initialized")
+logger.debug("aerOS API URL: %s", config.aerOS_API_URL)
+logger.debug("aerOS access token: %s", config.aerOS_ACCESS_TOKEN)
+logger.debug("aerOS debug mode: %s", config.DEBUG)
+logger.debug("aerOS log file: %s", config.LOG_FILE)
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/client.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/client.py
index f3efafa..ead33eb 100644
--- a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/client.py
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/client.py
@@ -1,29 +1,248 @@
-# Mocked API for testing purposes
-from typing import Dict, List, Optional
-from swagger_server.adapters.edgecloud.core.edgecloud_interface import EdgeCloudManagementInterface
+##
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Vasilis Pitsilis (vpitsilis@dat.demokritos.gr, vpitsilis@iit.demokritos.gr)
+# - Andreas Sakellaropoulos (asakellaropoulos@iit.demokritos.gr)
+##
+from typing import Any, Dict, List, Optional
+
+from src.edgecloud.clients.aeros import config
+from src.edgecloud.clients.aeros.continuum_client import ContinuumClient
+from src.edgecloud.core.edgecloud_interface import EdgeCloudManagementInterface
+from src.logger import setup_logger
+
class EdgeApplicationManager(EdgeCloudManagementInterface):
+ """
+ aerOS Continuum Client
+ FIXME: Handle None responses from continuum client
+ """
+
+ def __init__(self, base_url: str):
+ self.base_url = base_url
+ self.logger = setup_logger(__name__, is_debug=True, file_name=config.LOG_FILE)
+
def onboard_app(self, app_manifest: Dict) -> Dict:
- print(f"Submitting application: {app_manifest}")
- return {"appId": "1234-5678"}
+ # HLO-FE POST with TOSCA and app_id (service_id)
+ service_id = app_manifest.get("serviceId")
+ tosca_str = app_manifest.get("tosca")
+ aeros_client = ContinuumClient(self.base_url)
+ onboard_response = aeros_client.onboard_service(
+ service_id=service_id, tosca_str=tosca_str
+ )
+ return {"appId": onboard_response["serviceId"]}
def get_all_onboarded_apps(self) -> List[Dict]:
- return [{"appId": "1234-5678", "name": "TestApp"}]
+ aeros_client = ContinuumClient(self.base_url)
+ ngsild_params = "type=Service&format=simplified"
+ aeros_apps = aeros_client.query_entities(ngsild_params)
+ return [
+ {"appId": service["id"], "name": service["name"]} for service in aeros_apps
+ ]
+ # return [{"appId": "1234-5678", "name": "TestApp"}]
def get_onboarded_app(self, app_id: str) -> Dict:
- return {"appId": app_id, "name": "TestApp"}
+ aeros_client = ContinuumClient(self.base_url)
+ ngsild_params = "format=simplified"
+ aeros_app = aeros_client.query_entity(app_id, ngsild_params)
+ return {"appId": aeros_app["id"], "name": aeros_app["name"]}
def delete_onboarded_app(self, app_id: str) -> None:
print(f"Deleting application: {app_id}")
+ # TBD: Purge from continuum (make all ngsil-ld calls for servieId connected entities)
+ # Should check if undeployed first
def deploy_app(self, app_id: str, app_zones: List[Dict]) -> Dict:
- return {"appInstanceId": "abcd-efgh"}
+ # HLO-FE PUT with app_id (service_id)
+ aeros_client = ContinuumClient(self.base_url)
+ deploy_response = aeros_client.deploy_service(app_id)
+ return {"appInstanceId": deploy_response["serviceId"]}
+
+ def get_all_deployed_apps(
+ self,
+ app_id: Optional[str] = None,
+ app_instance_id: Optional[str] = None,
+ region: Optional[str] = None,
+ ) -> List[Dict]:
+ # FIXME: Get services in deployed state
+ aeros_client = ContinuumClient(self.base_url)
+ ngsild_params = 'type=Service&format=simplified&q=actionType=="DEPLOYED"'
+ if app_id:
+ ngsild_params += f'&q=service=="{app_id}"'
+ aeros_apps = aeros_client.query_entities(ngsild_params)
+ return [
+ {
+ "appInstanceId": service["id"],
+ "status":
+ # scomponent["serviceComponentStatus"].split(":")[-1].lower()
+ service["actionType"],
+ }
+ for service in aeros_apps
+ ]
+ # return [{"appInstanceId": "abcd-efgh", "status": "ready"}]
- def get_all_deployed_apps(self, app_id: Optional[str] = None, app_instance_id: Optional[str] = None, region: Optional[str] = None) -> List[Dict]:
- return [{"appInstanceId": "abcd-efgh", "status": "ready"}]
+ # def get_all_deployed_apps(self,
+ # app_id: Optional[str] = None,
+ # app_instance_id: Optional[str] = None,
+ # region: Optional[str] = None) -> List[Dict]:
+ # # FIXME: Get services in deployed state
+ # aeros_client = ContinuumClient(self.base_url)
+ # ngsild_params = "type=ServiceComponent&format=simplified"
+ # if app_id:
+ # ngsild_params += f'&q=service=="{app_id}"'
+ # aeros_apps = aeros_client.query_entities(ngsild_params)
+ # return [{
+ # "appInstanceId":
+ # scomponent["id"],
+ # "status":
+ # scomponent["serviceComponentStatus"].split(":")[-1].lower()
+ # } for scomponent in aeros_apps]
+ # # return [{"appInstanceId": "abcd-efgh", "status": "ready"}]
def undeploy_app(self, app_instance_id: str) -> None:
- print(f"Deleting app instance: {app_instance_id}")
+ # HLO-FE DELETE with app_id (service_id)
+ aeros_client = ContinuumClient(self.base_url)
+ _ = aeros_client.undeploy_service(app_instance_id)
+
+ def get_edge_cloud_zones(
+ self, region: Optional[str] = None, status: Optional[str] = None
+ ) -> List[Dict]:
+ aeros_client = ContinuumClient(self.base_url)
+ ngsild_params = "type=Domain&format=simplified"
+ aeros_domains = aeros_client.query_entities(ngsild_params)
+ return [
+ {
+ "edgeCloudZoneId": domain["id"],
+ "status": domain["domainStatus"].split(":")[-1].lower(),
+ }
+ for domain in aeros_domains
+ ]
+
+ # return [{"edgeCloudZoneId": "zone-1", "status": "active"}]
+
+ def get_edge_cloud_zones_details(
+ self, zone_id: str, flavour_id: Optional[str] = None
+ ) -> Dict:
+ """
+ Get details of a specific edge cloud zone.
+ :param zone_id: The ID of the edge cloud zone
+ :param flavour_id: Optional flavour ID to filter the results
+ :return: Details of the edge cloud zone
+ """
+ # Minimal mocked response based on required fields of 'ZoneRegisteredData' in GSMA OPG E/WBI API
+ # return {
+ # "zoneId":
+ # zone_id,
+ # "reservedComputeResources": [{
+ # "cpuArchType": "ISA_X86_64",
+ # "numCPU": "4",
+ # "memory": 8192,
+ # }],
+ # "computeResourceQuotaLimits": [{
+ # "cpuArchType": "ISA_X86_64",
+ # "numCPU": "8",
+ # "memory": 16384,
+ # }],
+ # "flavoursSupported": [{
+ # "flavourId":
+ # "medium-x86",
+ # "cpuArchType":
+ # "ISA_X86_64",
+ # "supportedOSTypes": [{
+ # "architecture": "x86_64",
+ # "distribution": "UBUNTU",
+ # "version": "OS_VERSION_UBUNTU_2204_LTS",
+ # "license": "OS_LICENSE_TYPE_FREE",
+ # }],
+ # "numCPU":
+ # 4,
+ # "memorySize":
+ # 8192,
+ # "storageSize":
+ # 100,
+ # }],
+ # #
+ # }
+ aeros_client = ContinuumClient(self.base_url)
+ ngsild_params = (
+ f'format=simplified&type=InfrastructureElement&q=domain=="{zone_id}"'
+ )
+ self.logger.debug(
+ "Querying infrastructure elements for zone %s with params: %s",
+ zone_id,
+ ngsild_params,
+ )
+ # Query the infrastructure elements for the specified zonese
+ aeros_domain_ies = aeros_client.query_entities(ngsild_params)
+ # Transform the infrastructure elements into the required format
+ # and return the details of the edge cloud zone
+ response = self.transform_infrastructure_elements(
+ domain_ies=aeros_domain_ies, domain=zone_id
+ )
+ self.logger.debug("Transformed response: %s", response)
+ # Return the transformed response
+ return response
+
+ def transform_infrastructure_elements(
+ self, domain_ies: List[Dict[str, Any]], domain: str
+ ) -> Dict[str, Any]:
+ """
+ Transform the infrastructure elements into a format suitable for the
+ edge cloud zone details.
+ :param domain_ies: List of infrastructure elements
+ :param domain: The ID of the edge cloud zone
+ :return: Transformed details of the edge cloud zone
+ """
+ total_cpu = 0
+ total_ram = 0
+ total_disk = 0
+ total_available_ram = 0
+ total_available_disk = 0
+
+ flavours_supported = []
+
+ for element in domain_ies:
+ total_cpu += element.get("cpuCores", 0)
+ total_ram += element.get("ramCapacity", 0)
+ total_available_ram += element.get("availableRam", 0)
+ total_disk += element.get("diskCapacity", 0)
+ total_available_disk += element.get("availableDisk", 0)
+
+ # Create a flavour per machine
+ flavour = {
+ "flavourId": f"{element.get('hostname')}-{element.get('containerTechnology')}",
+ "cpuArchType": f"{element.get('cpuArchitecture')}",
+ "supportedOSTypes": [
+ {
+ "architecture": f"{element.get('cpuArchitecture')}",
+ "distribution": f"{element.get('operatingSystem')}", # assume
+ "version": "OS_VERSION_UBUNTU_2204_LTS",
+ "license": "OS_LICENSE_TYPE_FREE",
+ }
+ ],
+ "numCPU": element.get("cpuCores", 0),
+ "memorySize": element.get("ramCapacity", 0),
+ "storageSize": element.get("diskCapacity", 0),
+ }
+ flavours_supported.append(flavour)
- def get_edge_cloud_zones(self, region: Optional[str] = None, status: Optional[str] = None) -> List[Dict]:
- return [{"edgeCloudZoneId": "zone-1", "status": "active"}]
+ result = {
+ "zoneId": domain,
+ "reservedComputeResources": [
+ {
+ "cpuArchType": "ISA_X86_64",
+ "numCPU": str(total_cpu),
+ "memory": total_ram,
+ }
+ ],
+ "computeResourceQuotaLimits": [
+ {
+ "cpuArchType": "ISA_X86_64",
+ "numCPU": str(total_cpu * 2), # Assume quota is 2x total?
+ "memory": total_ram * 2,
+ }
+ ],
+ "flavoursSupported": flavours_supported,
+ }
+ return result
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/config.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/config.py
new file mode 100644
index 0000000..e11c9c6
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/config.py
@@ -0,0 +1,24 @@
+##
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Vasilis Pitsilis (vpitsilis@dat.demokritos.gr, vpitsilis@iit.demokritos.gr)
+# - Andreas Sakellaropoulos (asakellaropoulos@iit.demokritos.gr)
+##
+"""
+aerOS access configuration
+Access tokens need to be provided in environment variables.
+"""
+import os
+
+aerOS_API_URL = os.environ.get("aerOS_API_URL")
+if not aerOS_API_URL:
+ raise ValueError("Environment variable 'aerOS_API_URL' is not set.")
+aerOS_ACCESS_TOKEN = os.environ.get("aerOS_ACCESS_TOKEN")
+if not aerOS_ACCESS_TOKEN:
+ raise ValueError("Environment variable 'aerOS_ACCESS_TOKEN' is not set.")
+aerOS_HLO_TOKEN = os.environ.get("aerOS_HLO_TOKEN")
+if not aerOS_HLO_TOKEN:
+ raise ValueError("Environment variable 'aerOS_HLO_TOKEN' is not set.")
+DEBUG = True
+LOG_FILE = ".log/aeros_client.log"
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/continuum_client.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/continuum_client.py
new file mode 100644
index 0000000..064ad6e
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/continuum_client.py
@@ -0,0 +1,170 @@
+##
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Vasilis Pitsilis (vpitsilis@dat.demokritos.gr, vpitsilis@iit.demokritos.gr)
+# - Andreas Sakellaropoulos (asakellaropoulos@iit.demokritos.gr)
+##
+"""
+aerOS REST API Client
+ This client is used to interact with the aerOS REST API.
+"""
+
+import requests
+
+from src.edgecloud.clients.aeros import config
+from src.edgecloud.clients.aeros.utils import catch_requests_exceptions
+from src.logger import setup_logger
+
+
+class ContinuumClient:
+ """
+ Client to aerOS ngsi-ld based continuum exposure
+ """
+
+ def __init__(self, base_url: str = None):
+ """
+ :param base_url: the base url of the aerOS API
+ """
+ if base_url is None:
+ self.api_url = config.aerOS_API_URL
+ else:
+ self.api_url = base_url
+ self.logger = setup_logger(__name__, is_debug=True, file_name=config.LOG_FILE)
+ self.m2m_cb_token = config.aerOS_ACCESS_TOKEN
+ self.hlo_token = config.aerOS_HLO_TOKEN
+ self.headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "aerOS": "true",
+ "Authorization": f"Bearer {self.m2m_cb_token}",
+ }
+ self.hlo_headers = {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ "aerOS": "true",
+ "Authorization": f"Bearer {self.hlo_token}",
+ }
+ self.hlo_onboard_headers = {
+ "Content-Type": "application/yaml",
+ "Authorization": f"Bearer {self.hlo_token}",
+ }
+
+ @catch_requests_exceptions
+ def query_entity(self, entity_id, ngsild_params) -> dict:
+ """
+ Query entity with ngsi-ld params
+ :input
+ @param entity_id: the id of the queried entity
+ @param ngsi-ld: the query params
+ :output
+ ngsi-ld object
+ """
+ entity_url = f"{self.api_url}/entities/{entity_id}?{ngsild_params}"
+ response = requests.get(entity_url, headers=self.headers, timeout=15)
+ if response is None:
+ return None
+ else:
+ if config.DEBUG:
+ self.logger.debug("Query entity URL: %s", entity_url)
+ self.logger.debug(
+ "Query entity response: %s %s", response.status_code, response.text
+ )
+ return response.json()
+
+ @catch_requests_exceptions
+ def query_entities(self, ngsild_params):
+ """
+ Query entities with ngsi-ld params
+ :input
+ @param ngsi-ld: the query params
+ :output
+ ngsi-ld object
+ """
+ entities_url = f"{self.api_url}/entities?{ngsild_params}"
+ response = requests.get(entities_url, headers=self.headers, timeout=15)
+ if response is None:
+ return None
+ # else:
+ # if config.DEBUG:
+ # self.logger.debug("Query entities URL: %s", entities_url)
+ # self.logger.debug("Query entities response: %s %s",
+ # response.status_code, response.text)
+ return response.json()
+
+ @catch_requests_exceptions
+ def deploy_service(self, service_id: str) -> dict:
+ """
+ Re-allocate (deploy) service on aerOS continuum
+ :input
+ @param service_id: the id of the service to be re-allocated
+ :output
+ the re-allocated service json object
+ """
+ re_allocate_url = f"{self.api_url}/hlo_fe/services/{service_id}"
+ response = requests.put(re_allocate_url, headers=self.hlo_headers, timeout=15)
+ if response is None:
+ return None
+ else:
+ if config.DEBUG:
+ self.logger.debug("Re-allocate service URL: %s", re_allocate_url)
+ self.logger.debug(
+ "Re-allocate service response: %s %s",
+ response.status_code,
+ response.text,
+ )
+ return response.json()
+
+ @catch_requests_exceptions
+ def undeploy_service(self, service_id: str) -> dict:
+ """
+ Undeploy service
+ :input
+ @param service_id: the id of the service to be undeployed
+ :output
+ the undeployed service json object
+ """
+ undeploy_url = f"{self.api_url}/hlo_fe/services/{service_id}"
+ response = requests.delete(undeploy_url, headers=self.hlo_headers, timeout=15)
+ if response is None:
+ return None
+ else:
+ if config.DEBUG:
+ self.logger.debug("Re-allocate service URL: %s", undeploy_url)
+ self.logger.debug(
+ "Undeploy service response: %s %s",
+ response.status_code,
+ response.text,
+ )
+ return response.json()
+
+ @catch_requests_exceptions
+ def onboard_service(self, service_id: str, tosca_str: str) -> dict:
+ """
+ Onboard (& deploy) service on aerOS continuum
+ :input
+ @param service_id: the id of the service to onboarded (& deployed)
+ @param tosca_str: the tosca whith all orchestration information
+ :output
+ the allocated service json object
+ """
+ onboard_url = f"{self.api_url}/hlo_fe/services/{service_id}"
+ if config.DEBUG:
+ self.logger.debug("Onboard service URL: %s", onboard_url)
+ self.logger.debug(
+ "Onboard service request body (TOSCA-YAML): %s", tosca_str
+ )
+ response = requests.post(
+ onboard_url, data=tosca_str, headers=self.hlo_onboard_headers, timeout=15
+ )
+ if response is None:
+ return None
+ else:
+ if config.DEBUG:
+ self.logger.debug("Onboard service URL: %s", onboard_url)
+ self.logger.debug(
+ "Onboard service response: %s %s",
+ response.status_code,
+ response.text,
+ )
+ return response.json()
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/utils.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/utils.py
new file mode 100644
index 0000000..d4f5cf5
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/aeros/utils.py
@@ -0,0 +1,43 @@
+##
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Vasilis Pitsilis (vpitsilis@dat.demokritos.gr, vpitsilis@iit.demokritos.gr)
+# - Andreas Sakellaropoulos (asakellaropoulos@iit.demokritos.gr)
+##
+"""
+Docstring
+"""
+from requests.exceptions import HTTPError, RequestException, Timeout
+
+import src.edgecloud.clients.aeros.config as config
+from src.logger import setup_logger
+
+
+def catch_requests_exceptions(func):
+ """
+ Docstring
+ """
+ logger = setup_logger(__name__, is_debug=True, file_name=config.LOG_FILE)
+
+ def wrapper(*args, **kwargs):
+ try:
+ result = func(*args, **kwargs)
+ return result
+ except HTTPError as e:
+ logger.info("4xx or 5xx: %s \n", {e})
+ return None # raise our custom exception or log, etc.
+ except ConnectionError as e:
+ logger.info(
+ "Raised for connection-related issues (e.g., DNS resolution failure, network issues): %s \n",
+ {e},
+ )
+ return None # raise our custom exception or log, etc.
+ except Timeout as e:
+ logger.info("Timeout occured: %s \n", {e})
+ return None # raise our custom exception or log, etc.
+ except RequestException as e:
+ logger.info("Request failed: %s \n", {e})
+ return None # raise our custom exception or log, etc.
+
+ return wrapper
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/client.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/client.py
index f3efafa..5396fd6 100644
--- a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/client.py
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/client.py
@@ -1,29 +1,197 @@
-# Mocked API for testing purposes
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+##
+# Copyright 2025-present by Software Networks Area, i2CAT.
+# All rights reserved.
+#
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Adrián Pino Martínez (adrian.pino@i2cat.net)
+# - Sergio Giménez (sergio.gimenez@i2cat.net)
+##
from typing import Dict, List, Optional
-from swagger_server.adapters.edgecloud.core.edgecloud_interface import EdgeCloudManagementInterface
+
+from src import logger
+from src.edgecloud.core.edgecloud_interface import EdgeCloudManagementInterface
+
+from . import schemas
+from .common import (
+ I2EdgeError,
+ i2edge_delete,
+ i2edge_get,
+ i2edge_post,
+ i2edge_post_multiform_data,
+)
+
+log = logger.get_logger(__name__)
+
class EdgeApplicationManager(EdgeCloudManagementInterface):
+ def __init__(self, base_url: str):
+ self.base_url = base_url
+
+ def get_edge_cloud_zones(
+ self, region: Optional[str] = None, status: Optional[str] = None
+ ) -> list[dict]:
+ url = "{}/zones/list".format(self.base_url)
+ params = {}
+ try:
+ response = i2edge_get(url, params=params)
+ log.info("Availability zones retrieved successfully")
+ return response
+ except I2EdgeError as e:
+ raise e
+
+ # Harcoded
+ def get_edge_cloud_zones_details(
+ self, zone_id: str, flavour_id: Optional[str] = None
+ ) -> Dict:
+ # Minimal mocked response based on required fields of 'ZoneRegisteredData' in GSMA OPG E/WBI API
+ return {
+ "zoneId": zone_id,
+ "reservedComputeResources": [
+ {
+ "cpuArchType": "ISA_X86_64",
+ "numCPU": "4",
+ "memory": 8192,
+ }
+ ],
+ "computeResourceQuotaLimits": [
+ {
+ "cpuArchType": "ISA_X86_64",
+ "numCPU": "8",
+ "memory": 16384,
+ }
+ ],
+ "flavoursSupported": [
+ {
+ "flavourId": "medium-x86",
+ "cpuArchType": "ISA_X86_64",
+ "supportedOSTypes": [
+ {
+ "architecture": "x86_64",
+ "distribution": "UBUNTU",
+ "version": "OS_VERSION_UBUNTU_2204_LTS",
+ "license": "OS_LICENSE_TYPE_FREE",
+ }
+ ],
+ "numCPU": 4,
+ "memorySize": 8192,
+ "storageSize": 100,
+ }
+ ],
+ }
+
+ def _create_artefact(
+ self,
+ artefact_id: str,
+ artefact_name: str,
+ repo_name: str,
+ repo_type: str,
+ repo_url: str,
+ password: Optional[str] = None,
+ token: Optional[str] = None,
+ user_name: Optional[str] = None,
+ ):
+ repo_type = schemas.RepoType(repo_type)
+ url = "{}/artefact".format(self.base_url)
+ payload = schemas.ArtefactOnboarding(
+ artefact_id=artefact_id,
+ name=artefact_name,
+ repo_password=password,
+ repo_name=repo_name,
+ repo_type=repo_type,
+ repo_url=repo_url,
+ repo_token=token,
+ repo_user_name=user_name,
+ )
+ try:
+ i2edge_post_multiform_data(url, payload)
+ log.info("Artifact added successfully")
+ except I2EdgeError as e:
+ raise e
+
+ def _get_artefact(self, artefact_id: str) -> Dict:
+ url = "{}/artefact/{}".format(self.base_url, artefact_id)
+ try:
+ response = i2edge_get(url, artefact_id)
+ log.info("Artifact retrieved successfully")
+ return response
+ except I2EdgeError as e:
+ raise e
+
+ def _get_all_artefacts(self) -> List[Dict]:
+ url = "{}/artefact".format(self.base_url)
+ try:
+ response = i2edge_get(url, {})
+ log.info("Artifacts retrieved successfully")
+ return response
+ except I2EdgeError as e:
+ raise e
+
+ def _delete_artefact(self, artefact_id: str):
+ url = "{}/artefact".format(self.base_url)
+ try:
+ i2edge_delete(url, artefact_id)
+ log.info("Artifact deleted successfully")
+ except I2EdgeError as e:
+ raise e
+
def onboard_app(self, app_manifest: Dict) -> Dict:
- print(f"Submitting application: {app_manifest}")
- return {"appId": "1234-5678"}
+ try:
+ app_id = app_manifest["appId"]
+ artefact_id = app_id
- def get_all_onboarded_apps(self) -> List[Dict]:
- return [{"appId": "1234-5678", "name": "TestApp"}]
+ app_component_spec = schemas.AppComponentSpec(artefactId=artefact_id)
+ data = schemas.ApplicationOnboardingData(
+ app_id=app_id, appComponentSpecs=[app_component_spec]
+ )
+ payload = schemas.ApplicationOnboardingRequest(profile_data=data)
+ url = "{}/application/onboarding".format(self.base_url)
+ i2edge_post(url, payload)
+ except I2EdgeError as e:
+ raise e
+ except KeyError as e:
+ raise I2EdgeError("Missing required field in app_manifest: {}".format(e))
+
+ def delete_onboarded_app(self, app_id: str) -> None:
+ url = "{}/application/onboarding".format(self.base_url)
+ try:
+ i2edge_delete(url, app_id)
+ except I2EdgeError as e:
+ raise e
def get_onboarded_app(self, app_id: str) -> Dict:
- return {"appId": app_id, "name": "TestApp"}
+ url = "{}/application/onboarding/{}".format(self.base_url, app_id)
+ try:
+ response = i2edge_get(url, app_id)
+ return response
+ except I2EdgeError as e:
+ raise e
- def delete_onboarded_app(self, app_id: str) -> None:
- print(f"Deleting application: {app_id}")
+ def get_all_onboarded_apps(self) -> List[Dict]:
+ url = "{}/applications/onboarding".format(self.base_url)
+ params = {}
+ try:
+ response = i2edge_get(url, params)
+ return response
+ except I2EdgeError as e:
+ raise e
+ # Harcoded
def deploy_app(self, app_id: str, app_zones: List[Dict]) -> Dict:
return {"appInstanceId": "abcd-efgh"}
- def get_all_deployed_apps(self, app_id: Optional[str] = None, app_instance_id: Optional[str] = None, region: Optional[str] = None) -> List[Dict]:
+ # Harcoded
+ def get_all_deployed_apps(
+ self,
+ app_id: Optional[str] = None,
+ app_instance_id: Optional[str] = None,
+ region: Optional[str] = None,
+ ) -> List[Dict]:
return [{"appInstanceId": "abcd-efgh", "status": "ready"}]
+ # Harcoded
def undeploy_app(self, app_instance_id: str) -> None:
print(f"Deleting app instance: {app_instance_id}")
-
- def get_edge_cloud_zones(self, region: Optional[str] = None, status: Optional[str] = None) -> List[Dict]:
- return [{"edgeCloudZoneId": "zone-1", "status": "active"}]
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/common.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/common.py
new file mode 100644
index 0000000..d4cda49
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/common.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+##
+# Copyright 2025-present by Software Networks Area, i2CAT.
+# All rights reserved.
+#
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Sergio Giménez (sergio.gimenez@i2cat.net)
+##
+import json
+from typing import Optional
+
+import requests
+from pydantic import BaseModel
+
+from src import logger
+from src.edgecloud.clients.errors import EdgeCloudPlatformError
+
+log = logger.get_logger(__name__)
+
+
+class I2EdgeError(EdgeCloudPlatformError):
+ pass
+
+
+class I2EdgeErrorResponse(BaseModel):
+ message: str
+ detail: dict
+
+
+def get_error_message_from(response: requests.Response) -> str:
+ try:
+ error_response = I2EdgeErrorResponse(**response.json())
+ return error_response.message
+ except Exception as e:
+ log.error("Failed to parse error response from i2edge: {}".format(e))
+ return response.text
+
+
+def i2edge_post(url: str, model_payload: BaseModel) -> dict:
+ headers = {
+ "Content-Type": "application/json",
+ "accept": "application/json",
+ }
+ json_payload = json.dumps(model_payload.model_dump(mode="json"))
+ try:
+ response = requests.post(url, data=json_payload, headers=headers)
+ response.raise_for_status()
+ return response.json()
+ except requests.exceptions.HTTPError as e:
+ i2edge_err_msg = get_error_message_from(response)
+ err_msg = "Failed to deploy app: {}. Detail: {}".format(i2edge_err_msg, e)
+ log.error(err_msg)
+ raise I2EdgeError(err_msg)
+
+
+def i2edge_post_multiform_data(url: str, model_payload: BaseModel) -> dict:
+ headers = {
+ "accept": "application/json",
+ }
+ payload_dict = model_payload.model_dump(mode="json")
+ payload_in_str = {k: str(v) for k, v in payload_dict.items()}
+ try:
+ response = requests.post(url, data=payload_in_str, headers=headers)
+ response.raise_for_status()
+ return response.json()
+ except requests.exceptions.HTTPError as e:
+ i2edge_err_msg = get_error_message_from(response)
+ err_msg = "Failed to deploy app: {}. Detail: {}".format(i2edge_err_msg, e)
+ log.error(err_msg)
+ raise I2EdgeError(err_msg)
+
+
+def i2edge_delete(url: str, id: str) -> dict:
+ headers = {"accept": "application/json"}
+ try:
+ query = "{}/{}".format(url, id)
+ response = requests.delete(query, headers=headers)
+ response.raise_for_status()
+ return response.json()
+ except requests.exceptions.HTTPError as e:
+ i2edge_err_msg = get_error_message_from(response)
+ err_msg = "Failed to undeploy app: {}. Detail: {}".format(i2edge_err_msg, e)
+ log.error(err_msg)
+ raise I2EdgeError(err_msg)
+
+
+def i2edge_get(url: str, params: Optional[dict]):
+ headers = {"accept": "application/json"}
+ try:
+ response = requests.get(url, params=params, headers=headers)
+ response.raise_for_status()
+ return response.json()
+ except requests.exceptions.HTTPError as e:
+ i2edge_err_msg = get_error_message_from(response)
+ err_msg = "Failed to get apps: {}. Detail: {}".format(i2edge_err_msg, e)
+ log.error(err_msg)
+ raise I2EdgeError(err_msg)
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/schemas.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/schemas.py
new file mode 100644
index 0000000..ed1a15d
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/schemas.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+##
+# Copyright 2025-present by Software Networks Area, i2CAT.
+# All rights reserved.
+#
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Sergio Giménez (sergio.gimenez@i2cat.net)
+# - César Cajas (cesar.cajas@i2cat.net)
+##
+from enum import Enum
+from typing import List, Optional
+
+from pydantic import BaseModel, ConfigDict, Field, field_validator
+
+
+class ZoneInfo(BaseModel):
+ flavourId: str
+ zoneId: str
+
+
+class AppParameters(BaseModel):
+ namespace: Optional[str] = None
+
+
+class AppDeployData(BaseModel):
+ appId: str
+ appProviderId: str
+ appVersion: str
+ zoneInfo: ZoneInfo
+
+
+class AppDeploy(BaseModel):
+ app_deploy_data: AppDeployData
+ app_parameters: Optional[AppParameters] = None
+
+
+# Artefact
+
+
+class RepoType(str, Enum):
+ UPLOAD = "UPLOAD"
+ PUBLICREPO = "PUBLICREPO"
+ PRIVATEREPO = "PRIVATEREPO"
+
+
+class ArtefactOnboarding(BaseModel):
+ artefact_id: str
+ name: str
+ # chart: Optional[bytes] = Field(default=None) # XXX AFAIK not supported by CAMARA.
+ repo_password: Optional[str] = None
+ repo_name: Optional[str] = None
+ repo_type: RepoType
+ repo_url: Optional[str] = None
+ repo_token: Optional[str] = None
+ repo_user_name: Optional[str] = None
+ model_config = ConfigDict(use_enum_values=True)
+
+
+# Application Onboarding
+
+# XXX Leaving default values since i2edge only cares about appid and artifactid, at least for now.
+
+
+class AppComponentSpec(BaseModel):
+ artefactId: str
+ componentName: str = Field(default="default_component")
+ serviceNameEW: str = Field(default="default_ew_service")
+ serviceNameNB: str = Field(default="default_nb_service")
+
+
+class AppMetaData(BaseModel):
+ appDescription: str = Field(default="Default app description")
+ appName: str = Field(default="Default App")
+ category: str = Field(default="DEFAULT")
+ mobilitySupport: bool = Field(default=False)
+ version: str = Field(default="1.0")
+
+
+class AppQoSProfile(BaseModel):
+ appProvisioning: bool = Field(default=True)
+ bandwidthRequired: int = Field(default=1)
+ latencyConstraints: str = Field(default="NONE")
+ multiUserClients: str = Field(default="APP_TYPE_SINGLE_USER")
+ noOfUsersPerAppInst: int = Field(default=1)
+
+
+class ApplicationOnboardingData(BaseModel):
+ appComponentSpecs: List[AppComponentSpec]
+ appDeploymentZones: List[str] = Field(default=["default_zone"])
+ app_id: str
+ appMetaData: AppMetaData = Field(default_factory=AppMetaData)
+ appProviderId: str = Field(default="default_provider")
+ appQoSProfile: AppQoSProfile = Field(default_factory=AppQoSProfile)
+ appStatusCallbackLink: Optional[str] = None
+
+
+class ApplicationOnboardingRequest(BaseModel):
+ profile_data: ApplicationOnboardingData
+
+
+# Flavour
+
+
+class GPU(BaseModel):
+ gpuMemory: int = Field(default=0, description="GPU memory in MB")
+ gpuModeName: str = Field(default="", description="GPU mode name")
+ gpuVendorType: str = Field(
+ default="GPU_PROVIDER_NVIDIA", description="GPU vendor type"
+ )
+ numGPU: int = Field(..., description="Number of GPUs")
+
+
+class Hugepages(BaseModel):
+ number: int = Field(default=0, description="Number of hugepages")
+ pageSize: str = Field(default="2MB", description="Size of hugepages")
+
+
+class SupportedOSTypes(BaseModel):
+ architecture: str = Field(default="x86_64", description="OS architecture")
+ distribution: str = Field(default="RHEL", description="OS distribution")
+ license: str = Field(default="OS_LICENSE_TYPE_FREE", description="OS license type")
+ version: str = Field(default="OS_VERSION_UBUNTU_2204_LTS", description="OS version")
+
+
+class FlavourSupported(BaseModel):
+ cpuArchType: str = Field(default="ISA_X86", description="CPU architecture type")
+ cpuExclusivity: bool = Field(default=True, description="CPU exclusivity")
+ fpga: int = Field(default=0, description="Number of FPGAs")
+ gpu: Optional[List[GPU]] = Field(default=None, description="List of GPUs")
+ hugepages: List[Hugepages] = Field(
+ default_factory=lambda: [Hugepages()], description="List of hugepages"
+ )
+ memorySize: str = Field(..., description="Memory size (e.g., '1024MB' or '2GB')")
+ numCPU: int = Field(..., description="Number of CPUs")
+ storageSize: int = Field(default=0, description="Storage size in GB")
+ supportedOSTypes: List[SupportedOSTypes] = Field(
+ default_factory=lambda: [SupportedOSTypes()],
+ description="List of supported OS types",
+ )
+ vpu: int = Field(default=0, description="Number of VPUs")
+
+ @field_validator("memorySize")
+ @classmethod
+ def validate_memory_size(cls, v):
+ if not (v.endswith("MB") or v.endswith("GB")):
+ raise ValueError("memorySize must end with MB or GB")
+ try:
+ int(v[:-2])
+ except ValueError:
+ raise ValueError("memorySize must be a number followed by MB or GB")
+ return v
+
+
+class Flavour(BaseModel):
+ flavour_supported: FlavourSupported
+
+
+# EdgeCloud Zones
+
+
+class Zone(BaseModel):
+ geographyDetails: str
+ geolocation: str
+ zoneId: str
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/utils.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/utils.py
new file mode 100644
index 0000000..bee5e94
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/i2edge/utils.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+##
+# Copyright 2025-present by Software Networks Area, i2CAT.
+# All rights reserved.
+#
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Sergio Giménez (sergio.gimenez@i2cat.net)
+# - César Cajas (cesar.cajas@i2cat.net)
+##
+import uuid
+from typing import Optional, Union
+from uuid import UUID
+
+from src.edgecloud import logger
+from src.edgecloud.api.routers.lcm.schemas import RequiredResources
+from src.edgecloud.core import utils as core_utils
+
+from .client import I2EdgeClient
+from .common import I2EdgeError
+
+log = logger.get_logger(__name__)
+
+
+def generate_namespace_name_from(app_id: str, app_instance_id: str) -> str:
+ max_length = 63
+ combined_name = "{}-{}".format(app_id, app_instance_id)
+ if len(combined_name) > max_length:
+ combined_name = combined_name[:max_length]
+ return combined_name
+
+
+def generate_unique_id() -> UUID:
+ return uuid.uuid4()
+
+
+def instantiate_app_with(
+ camara_app_id: UUID,
+ zone_id: str,
+ required_resources: RequiredResources,
+ i2edge: I2EdgeClient,
+) -> tuple[str, str]:
+ memory_size_str = "{}GB".format(required_resources.memory + 1)
+ num_gpus = core_utils.get_num_gpus_from(required_resources)
+ try:
+ flavour_id = i2edge.create_flavour(
+ zone_id=zone_id,
+ memory_size=memory_size_str,
+ num_cpu=required_resources.numCPU,
+ num_gpus=num_gpus,
+ )
+ i2edge_instance_id = generate_unique_id()
+ application_k8s_namespace = generate_namespace_name_from(
+ str(camara_app_id), str(i2edge_instance_id)
+ )
+ i2edge.deploy_app(
+ appId=str(camara_app_id),
+ zoneId=zone_id,
+ flavourId=flavour_id,
+ namespace=application_k8s_namespace,
+ )
+ return flavour_id, application_k8s_namespace
+ except I2EdgeError as e:
+ err_msg = "Error instantiating app {} in zone {}".format(camara_app_id, zone_id)
+ log.error("{}. Detailed error: {}".format(err_msg, e))
+ raise e
+
+
+def onboard_app_with(
+ application_id: UUID,
+ artefact_id: UUID,
+ app_name: str,
+ app_version: Optional[str], # TODO pass this to i2edge
+ repo_type: str,
+ app_repo: str,
+ user_name: Optional[str],
+ password: Optional[str],
+ token: Optional[str],
+ i2edge: I2EdgeClient,
+):
+ try:
+ # TODO Come back to handle errors when onboarding and perform rollbacks
+ i2edge.create_artefact(
+ artefact_id=str(artefact_id),
+ artefact_name=app_name,
+ repo_name=app_name,
+ repo_type=repo_type,
+ repo_url=app_repo,
+ user_name=user_name,
+ password=password,
+ token=token,
+ )
+
+ i2edge.onboard_app(app_id=str(application_id), artefact_id=str(application_id))
+ except I2EdgeError as e:
+ err_msg = "Error onboarding app {} in i2edge".format(app_name)
+ log.error("{}. Detailed error: {}".format(err_msg, e))
+ raise e
+
+
+def delete_app_instance_by(
+ namespace: str, flavour_id: str, zone_id: str, i2edge: I2EdgeClient
+):
+ i2edge_app_instance_name = get_app_name_from(namespace, i2edge)
+ if i2edge_app_instance_name is None:
+ err_msg = "Couldn't retrieve app instance from I2Edge."
+ log.error(err_msg)
+ raise I2EdgeError(err_msg)
+ i2edge.undeploy_app(i2edge_app_instance_name)
+ i2edge.delete_flavour(flavour_id=str(flavour_id), zone_id=zone_id)
+
+
+def get_app_name_from(namespace: str, i2edge: I2EdgeClient) -> Union[str, None]:
+ try:
+ response = i2edge.get_all_deployed_apps()
+ for deployment in response:
+ if deployment.get("bodytosend", {}).get("namespace") == namespace:
+ return deployment.get("name")
+ return None
+ except I2EdgeError as e:
+ err_msg = "Error getting app name for namespace {}".format(namespace)
+ log.error("{}. Detailed error: {}".format(err_msg, e))
+ raise e
+
+
+def delete_app_by(app_id: UUID, artefact_id: UUID, i2edge: I2EdgeClient):
+ try:
+ i2edge.delete_onboarded_app(app_id=str(app_id))
+ i2edge.delete_artefact(artefact_id=str(artefact_id))
+ except I2EdgeError as e:
+ err_msg = "Error deleting app {}".format(app_id)
+ log.error("{}. Detailed error: {}".format(err_msg, e))
+ raise e
+
+
+def get_edgecloud_zones(i2edge: I2EdgeClient) -> list[str]:
+ try:
+ zone_ids = []
+ response = i2edge.get_zones_list()
+ for zone in response:
+ zone_id = zone.get("zoneId")
+ if zone_id is not None:
+ zone_ids.append(zone_id)
+ return zone_ids
+
+ except I2EdgeError as e:
+ err_msg = "Error getting zones from i2edge"
+ log.error("{}. Detailed error: {}".format(err_msg, e))
+ raise e
diff --git a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/piedge/client.py b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/piedge/client.py
index f3f04fe..d0a20ec 100644
--- a/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/piedge/client.py
+++ b/service-resource-manager-implementation/swagger_server/adapters/edgecloud/clients/piedge/client.py
@@ -30,29 +30,38 @@ class EdgeApplicationManager(EdgeCloudManagementInterface):
def get_all_onboarded_apps(self) -> List[Dict]:
logging.info('Retrieving all registered apps from database...')
- app_list = connector_db.get_documents_from_collection(collection_input="service_functions")
+ db_list = connector_db.get_documents_from_collection(collection_input="service_functions")
+ app_list = []
+ for sf in db_list:
+ app_list.append(self.__transform_to_camara(sf))
return app_list
# return [{"appId": "1234-5678", "name": "TestApp"}]
def get_onboarded_app(self, app_id: str) -> Dict:
logging.info('Searching for registered app with ID: '+ app_id+' in database...')
app = connector_db.get_documents_from_collection("service_functions", input_type="_id", input_value=app_id)
- return app
+ if len(app)>0:
+ return self.__transform_to_camara(app[0])
+ else:
+ return []
def delete_onboarded_app(self, app_id: str) -> None:
logging.info('Deleting registered app with ID: '+ app_id+' from database...')
result, code = connector_db.delete_document_service_function(_id=app_id)
+ print(f"Removing application metadata: {app_id}")
return result, code
- # print(f"Deleting application: {app_id}")
def deploy_app(self, app_id: str, app_zones: List[Dict]) -> Dict:
logging.info('Searching for registered app with ID: '+ app_id+' in database...')
app = connector_db.get_documents_from_collection("service_functions", input_type="_id", input_value=app_id)
success_response = []
+ if len(app)<1:
+ return 'Application with ID: '+ app_id+' not found', 404
if app is not None:
+ logging.info(app_zones)
for zone in app_zones:
sf = DeployServiceFunction(service_function_name=app[0].get('name'),
- service_function_instance_name=app[0].get('name')+'-'+zone.get('edgeCloudZoneName'),
+ service_function_instance_name=app[0].get('name')+'-'+zone.get('EdgeCloudZone').get('edgeCloudZoneName'),
location=zone.get('edgeCloudZoneName'))
result = deploy_service_function(service_function=sf)
success_response.append(result)
@@ -61,8 +70,18 @@ class EdgeApplicationManager(EdgeCloudManagementInterface):
def get_all_deployed_apps(self, app_id: Optional[str] = None, app_instance_id: Optional[str] = None, region: Optional[str] = None) -> List[Dict]:
logging.info('Retreiving all deployed apps in the edge cloud platform')
- response = kubernetes_connector.get_deployed_service_functions()
- return [{"appInstanceId": "abcd-efgh", "status": "ready"}]
+ deployments = kubernetes_connector.get_deployed_service_functions()
+ response = []
+ for deployment in deployments:
+ item = {}
+ item['appInstanceId'] = deployment.get('uid')
+ item['status'] = deployment.get('status')
+ item['componentEndpointInfo'] = {}
+ item['kubernetesClusterRef'] = ""
+ item['edgeCloudZone'] = {}
+ response.append(item)
+ return response
+ # return [{"appInstanceId": "abcd-efgh", "status": "ready"}]
def undeploy_app(self, app_instance_id: str) -> None:
logging.info('Searching for deployed app with ID: '+ app_instance_id+' in database...')
@@ -70,10 +89,12 @@ class EdgeApplicationManager(EdgeCloudManagementInterface):
# deployed_service_function_name_=auxiliary_functions.prepare_name_for_k8s(deployed_service_function_name)
sfs=kubernetes_connector.get_deployed_service_functions()
response = 'App instance with ID ['+app_instance_id+'] not found'
- for service_fun in sfs.items:
+ for service_fun in sfs:
if service_fun["uid"]==app_instance_id:
- response = kubernetes_connector.delete_service_function(service_fun['service_function_instance_name'])
- return response
+ kubernetes_connector.delete_service_function(service_fun['service_function_instance_name'])
+ response = 'App instance with ID ['+app_instance_id+'] successfully removed'
+ break
+ return response
def get_edge_cloud_zones(self, region: Optional[str] = None, status: Optional[str] = None) -> List[Dict]:
@@ -91,3 +112,16 @@ class EdgeApplicationManager(EdgeCloudManagementInterface):
zone_list.append(zone)
return zone_list
+ def __transform_to_camara(self, app_data):
+ app = {}
+ app['appId'] = app_data.get('_id')
+ app['name'] = app_data.get('name')
+ app['packageType'] = app_data.get('type')
+ appRepo = {'imagePath': app_data.get('image')}
+ app['appRepo'] = appRepo
+ networkInterfaces = []
+ for port in app_data.get('application_ports'):
+ port_spec = {'protocol': 'TCP', 'port': port}
+ networkInterfaces.append(port_spec)
+ app['componentSpec'] = [{'componentName': app_data.get('name'), 'networkInterfaces': networkInterfaces}]
+ return app
diff --git a/service-resource-manager-implementation/swagger_server/adapters/logger.py b/service-resource-manager-implementation/swagger_server/adapters/logger.py
new file mode 100644
index 0000000..4fb7825
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/logger.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+##
+# Copyright 2025-present by Software Networks Area, i2CAT.
+# All rights reserved.
+#
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Sergio Giménez (sergio.gimenez@i2cat.net)
+##
+import logging
+import sys
+
+from colorlog import ColoredFormatter
+
+APP_LOGGER_NAME = "edgecloud"
+COLORED_FORMATERR = (
+ "%(log_color)s%(levelname)s%(reset)s | "
+ "[%(log_color)s%(name)s%(reset)s:%(log_color)s%(lineno)d%(reset)s] "
+ "%(log_color)s%(message)s%(reset)s"
+)
+FILE_FORMATTER = "[%(asctime)s] {%(name)s: %(lineno)d} %(levelname)s - %(message)s"
+
+
+def setup_logger(logger_name=APP_LOGGER_NAME, is_debug=True, file_name=None):
+
+ logger = logging.getLogger(logger_name)
+ logger.setLevel(logging.DEBUG if is_debug else logging.INFO)
+
+ colored_formatter = ColoredFormatter(COLORED_FORMATERR)
+ file_formatter = logging.Formatter(FILE_FORMATTER)
+
+ sh = logging.StreamHandler(sys.stdout)
+ sh.setFormatter(colored_formatter)
+ logger.handlers.clear()
+ logger.addHandler(sh)
+
+ if file_name:
+ fh = logging.FileHandler(file_name)
+ fh.setFormatter(file_formatter)
+ logger.addHandler(fh)
+
+ return logger
+
+
+def get_logger(module_name):
+ return logging.getLogger(APP_LOGGER_NAME).getChild(module_name)
diff --git a/service-resource-manager-implementation/swagger_server/adapters/main.py b/service-resource-manager-implementation/swagger_server/adapters/main.py
new file mode 100644
index 0000000..6fede35
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/main.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+from src import logger
+from src.edgecloud.core.edgecloud_factory import EdgeCloudFactory
+
+logger.setup_logger(is_debug=True, file_name="sdk.log")
+
+
+def create_edgecloud_client(client_name: str, base_url: str):
+ """
+ Create and return an edgecloud client.
+
+ Args:
+ client_name (str): Name of the edge cloud platform. Must be one of:
+ 'i2edge', 'aeros', 'piedge'
+ base_url (str): The base URL for the client.
+
+ Returns:
+ The created edgecloud client.
+
+ Example:
+ >>> client = create_edgecloud_client('i2edge', 'http://localhost:8080')
+ """
+ return EdgeCloudFactory.create_edgecloud_client(client_name, base_url)
+
+
+# ###########################################
+# # Temporal code - Testing purposes
+# ###########################################
+# if __name__ == "__main__":
+# # Define the client name and base URL
+# client_name = "i2edge"
+# base_url = "http://192.168.123.237:30769/"
+
+# # Create the edgecloud client
+# sbi = EdgeCloudFactory.create_edgecloud_client(client_name, base_url)
+
+# # Print the edgecloud client being used and its URL
+# print(f"Using edgecloud client: {sbi}")
+# print(f"Edge Cloud Platform: {client_name}")
+# print(f"URL: {sbi.base_url}")
+# print("")
+
+# # Get all availability zones
+# print("Running test endpoint: get_edge_cloud_zones:")
+# zones = sbi.get_edge_cloud_zones()
+# print(zones)
+# ###########################################
+# # End of temporal code
+# ###########################################
diff --git a/service-resource-manager-implementation/swagger_server/adapters/artifact/clients/skopeo/.gitkeep b/service-resource-manager-implementation/swagger_server/adapters/network/__init__.py
similarity index 100%
rename from service-resource-manager-implementation/swagger_server/adapters/artifact/clients/skopeo/.gitkeep
rename to service-resource-manager-implementation/swagger_server/adapters/network/__init__.py
diff --git a/service-resource-manager-implementation/swagger_server/adapters/artifact/core/.gitkeep b/service-resource-manager-implementation/swagger_server/adapters/network/clients/__init__.py
similarity index 100%
rename from service-resource-manager-implementation/swagger_server/adapters/artifact/core/.gitkeep
rename to service-resource-manager-implementation/swagger_server/adapters/network/clients/__init__.py
diff --git a/service-resource-manager-implementation/swagger_server/adapters/network/clients/errors.py b/service-resource-manager-implementation/swagger_server/adapters/network/clients/errors.py
new file mode 100644
index 0000000..6497bd8
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/network/clients/errors.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+class NetworkPlatformError(Exception):
+ pass
diff --git a/service-resource-manager-implementation/swagger_server/adapters/network/clients/oai/client.py b/service-resource-manager-implementation/swagger_server/adapters/network/clients/oai/client.py
new file mode 100644
index 0000000..8bc3a56
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/network/clients/oai/client.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+from typing import Dict
+
+import logger
+from network.core.network_interface import NetworkManagementInterface
+
+log = logger.get_logger(__name__)
+
+
+# Placeholder for the OAI Network Management Client
+class NetworkManager(NetworkManagementInterface):
+ def __init__(self, base_url: str, scs_as_id: str):
+ pass
+
+ def create_qod_session(self, session_info: Dict) -> Dict:
+ pass
+
+ def get_qod_session(self, session_id: str) -> Dict:
+ pass
+
+ def delete_qod_session(self, session_id: str) -> None:
+ pass
+
+
+# Note:
+# As this class is inheriting from NetworkManagementInterface, it is
+# expected to implement all the abstract methods defined in that interface.
+#
+# In case this network adapter doesn't support a specific method, it should
+# be marked as NotImplementedError.
diff --git a/service-resource-manager-implementation/swagger_server/controllers/network_function_controller.py b/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gcore/__init__.py
similarity index 100%
rename from service-resource-manager-implementation/swagger_server/controllers/network_function_controller.py
rename to service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gcore/__init__.py
diff --git a/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gcore/client.py b/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gcore/client.py
new file mode 100644
index 0000000..195e676
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gcore/client.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+from typing import Dict
+
+from src import logger
+from src.network.core.network_interface import NetworkManagementInterface
+
+log = logger.get_logger(__name__)
+
+
+# Placeholder for the Open5gcore Network Management Client
+class NetworkManager(NetworkManagementInterface):
+ def __init__(self, base_url: str, scs_as_id: str):
+ pass
+
+ def create_qod_session(self, session_info: Dict) -> Dict:
+ pass
+
+ def get_qod_session(self, session_id: str) -> Dict:
+ pass
+
+ def delete_qod_session(self, session_id: str) -> None:
+ pass
+
+
+# Note:
+# As this class is inheriting from NetworkManagementInterface, it is
+# expected to implement all the abstract methods defined in that interface.
+#
+# In case this network adapter doesn't support a specific method, it should
+# be marked as NotImplementedError.
diff --git a/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/client.py b/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/client.py
new file mode 100644
index 0000000..422892f
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/client.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+from typing import Dict
+
+from src import logger
+from src.network.core.network_interface import NetworkManagementInterface
+
+log = logger.get_logger(__name__)
+
+
+class NetworkManager(NetworkManagementInterface):
+ """
+ This client implements the NetworkManagementInterface and translates the
+ CAMARA APIs into specific HTTP requests understandable by the Open5GS NEF API.
+
+ Invloved partners and their roles in this implementation:
+ - I2CAT: Responsible for the CAMARA QoD API and its mapping to the
+ 3GPP AsSessionWithQoS API exposed by Open5GS NEF.
+ - NCSRD: Responsible for the CAMARA Location API and its mapping to the
+ 3GPP Monitoring Event API exposed Open5GS NEF.
+ """
+
+ def __init__(self, base_url: str, scs_as_id: str):
+ """
+ Initializes the Open5GS Client.
+ """
+ try:
+ self.base_url = base_url
+ self.scs_as_id = scs_as_id
+ log.info(
+ f"Initialized Open5GSClient with base_url: {self.base_url} "
+ f"and scs_as_id: {self.scs_as_id}"
+ )
+ except Exception as e:
+ log.error(f"Failed to initialize Open5GSClient: {e}")
+ raise e
+
+ # --- Implementation of NetworkManagementInterface methods ---
+ def create_qod_session(self, session_info: Dict) -> Dict:
+ """
+ Creates a QoD session based on the CAMARA QoD API input.
+ Maps the CAMARA QoD POST /sessions to Open5GS NEF POST /{scsAsId}/subscriptions.
+ """
+ pass
+
+ def get_qod_session(self, session_id: str) -> Dict:
+ """
+ Retrieves a specific Open5GS QoS Subscription details.
+ Maps CAMARA QoD GET /sessions/{sessionId} to Open5GS NEF GET /
+ {scsAsId}/subscriptions/{subscriptionId}.
+ """
+ pass
+
+ def delete_qod_session(self, session_id: str) -> None:
+ """
+ Deletes a specific Open5GS QoS Subscription.
+ Maps CAMARA QoD DELETE /sessions/{sessionId} to Open5GS NEF DELETE /
+ {scsAsId}/subscriptions/{subscriptionId}.
+ """
+ pass
+
+
+# Note:
+# As this class is inheriting from NetworkManagementInterface, it is
+# expected to implement all the abstract methods defined in that interface.
+#
+# In case this network adapter doesn't support a specific method, it should
+# be marked as NotImplementedError.
diff --git a/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/common.py b/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/common.py
new file mode 100644
index 0000000..d0acc64
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/common.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# Common utilities (errors, HTTP helpers) used by the Open5GS client implementation (client.py).
+from typing import Optional
+
+from pydantic import BaseModel
+
+from src import logger
+from src.network.clients.errors import NetworkPlatformError
+
+log = logger.get_logger(__name__)
+
+
+class Open5GSError(NetworkPlatformError):
+ pass
+
+
+class Open5GSErrorResponse(BaseModel):
+ message: str
+ detail: dict
+
+
+# --- HTTP Request Helper Functions ---
+def open5gs_post(url: str, model_payload: BaseModel) -> dict:
+ """
+ Placeholder for the POST request function."""
+ pass
+
+
+def open5gs_get(url: str, params: Optional[dict] = None) -> dict:
+ """
+ Placeholder for the GET request function.
+ """
+ pass
+
+
+def open5gs_delete(url: str) -> None:
+ """
+ Placeholder for the DELETE request function.
+ """
+ pass
diff --git a/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/schemas.py b/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/schemas.py
new file mode 100644
index 0000000..b572d75
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/network/clients/open5gs/schemas.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# This file defines the Pydantic models that represent the data structures (schemas)
+# for the requests sent to and responses received from the Open5GS NEF API,
+# specifically focusing on the APIs needed to support CAMARA QoD.
+
+from pydantic import BaseModel
+
+
+# Dummy examples of Pydantic models for the Open5GS NEF API.
+class Open5GSQoSSubscription(BaseModel):
+ """
+ Represents the payload for creating a QoS subscription in Open5GS.
+ """
+
+ pass
+
+
+class CamaraQoDSessionInfo(BaseModel):
+ """
+ Represents the input data for creating a QoD session.
+ """
+
+ pass
diff --git a/service-resource-manager-implementation/swagger_server/adapters/network/core/__init__.py b/service-resource-manager-implementation/swagger_server/adapters/network/core/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/service-resource-manager-implementation/swagger_server/adapters/network/core/network_factory.py b/service-resource-manager-implementation/swagger_server/adapters/network/core/network_factory.py
new file mode 100644
index 0000000..9669b6c
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/network/core/network_factory.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+##
+# Copyright 2025-present by Software Networks Area, i2CAT.
+# All rights reserved.
+#
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Reza Mosahebfard (reza.mosahebfard@i2cat.net)
+##
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from src.network.clients.oai.client import OaiNefClient
+from src.network.clients.open5gcore.client import Open5GCoreClient
+from src.network.clients.open5gs.client import Open5GSClient
+
+if TYPE_CHECKING:
+ from .network_interface import NetworkManagementInterface
+
+
+class NetworkClientFactory:
+ """
+ Factory class for creating Network Management Clients.
+ """
+
+ @staticmethod
+ def create_network_client(
+ client_name: str, base_url: str
+ ) -> NetworkManagementInterface:
+ """
+ Creates and returns an instance of the specified Network Client.
+ """
+ try:
+ constructor = NetworkClientFactory.network_client_constructors[client_name]
+ network_client_instance = constructor(base_url)
+ return network_client_instance
+ except KeyError:
+ # Get the list of supported client names
+ supported_clients = list(
+ NetworkClientFactory.network_client_constructors.keys()
+ )
+ raise ValueError(
+ f"Invalid network client name: '{client_name}'. "
+ "Supported clients are: "
+ f"{', '.join(supported_clients)}"
+ )
+
+
+class NetworkClientTypes:
+ """
+ Class for creating Network Clients.
+ """
+
+ OPEN5GS = "open5gs"
+ OAI = "oai"
+ OPEN5GCORE = "open5gcore"
+
+ # --- Dictionary mapping type constants to constructors ---
+ network_types = {
+ OPEN5GS: lambda url: Open5GSClient(base_url=url),
+ OAI: lambda url: OaiNefClient(base_url=url),
+ OPEN5GCORE: lambda url: Open5GCoreClient(base_url=url),
+ }
diff --git a/service-resource-manager-implementation/swagger_server/adapters/network/core/network_interface.py b/service-resource-manager-implementation/swagger_server/adapters/network/core/network_interface.py
new file mode 100644
index 0000000..0628ca9
--- /dev/null
+++ b/service-resource-manager-implementation/swagger_server/adapters/network/core/network_interface.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+##
+# Copyright 2025-present by Software Networks Area, i2CAT.
+# All rights reserved.
+#
+# This file is part of the Open SDK
+#
+# Contributors:
+# - Reza Mosahebfard (reza.mosahebfard@i2cat.net)
+##
+from abc import ABC, abstractmethod
+from typing import Dict
+
+
+class NetworkManagementInterface(ABC):
+ """
+ Abstract Base Class for Network Resource Management.
+
+ This interface defines the standard methods that all
+ Network Clients (Open5GS, OAI, Open5GCore) must implement.
+
+ Partners implementing a new network client should inherit from this class
+ and provide concrete implementations for all abstract methods relevant
+ to their specific NEF capabilities.
+ """
+
+ @abstractmethod
+ def create_qod_session(self, session_info: Dict) -> Dict:
+ """
+ Creates a QoS session based on CAMARA QoD API input.
+
+ args:
+ session_info: Dictionary containing session details conforming to
+ the CAMARA QoD session creation parameters.
+
+ returns:
+ dictionary containing the created session details, including its ID.
+ """
+ pass
+
+ @abstractmethod
+ def get_qod_session(self, session_id: str) -> Dict:
+ """
+ Retrieves details of a specific Quality on Demand (QoS) session.
+
+ args:
+ session_id: The unique identifier of the QoS session.
+
+ returns:
+ Dictionary containing the details of the requested QoS session.
+ """
+ pass
+
+ @abstractmethod
+ def delete_qod_session(self, session_id: str) -> None:
+ """
+ Deletes a specific Quality on Demand (QoS) session.
+
+ args:
+ session_id: The unique identifier of the QoS session to delete.
+
+ returns:
+ None
+ """
+ pass
+
+ # Placeholder for other CAMARA APIs (e.g., Traffic Influence,
+ # Location-retrieval, etc.)
diff --git a/service-resource-manager-implementation/swagger_server/controllers/network_functions_controller.py b/service-resource-manager-implementation/swagger_server/controllers/network_functions_controller.py
index 3447b16..a10f5a4 100644
--- a/service-resource-manager-implementation/swagger_server/controllers/network_functions_controller.py
+++ b/service-resource-manager-implementation/swagger_server/controllers/network_functions_controller.py
@@ -1,5 +1,61 @@
from os import environ
import logging
+import connexion
logger = logging.getLogger(__name__)
-logging.basicConfig(level=logging.DEBUG)
\ No newline at end of file
+logging.basicConfig(level=logging.DEBUG)
+
+
+network_client = environ.get('NETWORK_CLIENT')
+adapter = None
+
+if network_client is not None:
+ if network_client=='oai':
+ from swagger_server.adapters.network.clients.oai.client import NetworkManager
+ adapter = NetworkManager()
+ elif network_client=='open5gcore':
+ from swagger_server.adapters.network.clients.open5gcore.client import NetworkManager
+ adapter = NetworkManager()
+ else:
+ from swagger_server.adapters.network.clients.open5gs.client import NetworkManager
+ adapter = NetworkManager()
+
+
+def create_qod_session(body):
+ if connexion.request.is_json:
+ try:
+ response = adapter.create_qod_session(body)
+ return response
+ except Exception as ce_:
+ logger.error(ce_)
+ return ce_
+ else:
+ return 'ERROR: Could not read JSON payload.', 400
+
+def get_qod_session(id: str):
+ try:
+ response = adapter.get_qod_session(id)
+ return response
+ except Exception as ce_:
+ logger.error(ce_)
+ return ce_
+
+def delete_qod_session(id: str):
+ try:
+ response = adapter.delete_qod_session(id)
+ return 'QoD successfully removed'
+ except Exception as ce_:
+ logger.error(ce_)
+ return ce_
+
+def create_traffic_influence_resource(body):
+ pass
+
+def delete_traffic_influence_resource(id: str):
+ pass
+
+def get_traffic_influence_resource(id: str):
+ pass
+
+def get_all_traffic_influence_resources():
+ pass
\ No newline at end of file
diff --git a/service-resource-manager-implementation/swagger_server/controllers/service_functions_instances_controller.py b/service-resource-manager-implementation/swagger_server/controllers/service_functions_instances_controller.py
index 76319a3..5cd47f1 100644
--- a/service-resource-manager-implementation/swagger_server/controllers/service_functions_instances_controller.py
+++ b/service-resource-manager-implementation/swagger_server/controllers/service_functions_instances_controller.py
@@ -164,17 +164,17 @@ def deployed_service_functions_status(): # noqa: E501
:rtype: DeployedappsResponse
"""
- role = user_authentication.check_role()
- if role is not None and role == "admin":
- try:
+ # role = user_authentication.check_role()
+ # if role is not None and role == "admin":
+ try:
# response = kubernetes_connector.get_deployed_service_functions()
- response = adapter.get_all_deployed_apps()
- return response
- except Exception as ce_:
- logger.error(ce_)
- return ce_
- else:
- return "You are not authorized to access the URL requested", 401
+ response = adapter.get_all_deployed_apps()
+ return response
+ except Exception as ce_:
+ logger.error(ce_)
+ return ce_
+ # else:
+ # return "You are not authorized to access the URL requested", 401
def update_chain(body=None): # noqa: E501
diff --git a/service-resource-manager-implementation/swagger_server/swagger/swagger.yaml b/service-resource-manager-implementation/swagger_server/swagger/swagger.yaml
index fba02e0..ac98a9a 100644
--- a/service-resource-manager-implementation/swagger_server/swagger/swagger.yaml
+++ b/service-resource-manager-implementation/swagger_server/swagger/swagger.yaml
@@ -73,11 +73,11 @@ paths:
"405":
description: Method not allowed
x-openapi-router-controller: swagger_server.controllers.operations_controller
- /copy-artifact:
+ /copy-artefact:
post:
tags:
- - Artifact Management
- summary: Copies artifact from source repository to destination repository
+ - Artefact Management
+ summary: Copies artefact from source repository to destination repository
operationId: copy_artifact
requestBody:
description: Artifact details including image name, tag, source repository username and password tec
@@ -91,14 +91,14 @@ paths:
"400":
description: Mandatory fields missing
x-openapi-router-controller: swagger_server.controllers.artifact_controller
- /artifact-exists:
+ /artefact-exists:
post:
tags:
- - Artifact Management
- summary: Check if artifact exists in given repository
+ - Artefact Management
+ summary: Check if artefact exists in given repository
operationId: artifact_exists
requestBody:
- description: Artifact details including image name, tag, source repository username and password tec
+ description: Artefact details including image name, tag, source repository username and password tec
content:
application/json:
schema:
@@ -687,7 +687,7 @@ paths:
"200":
description: App updated.
x-openapi-router-controller: swagger_server.controllers.service_functions_instances_controller
- /deployedServiceFunction/{deployedServiceFunctionName}:
+ /deployedServiceFunction/{app_id}:
get:
tags:
- Service Functions Instances
@@ -696,7 +696,7 @@ paths:
# security:
# - jwt: [ ]
parameters:
- - name: deployedServiceFunctionName
+ - name: app_id
in: path
description: Represents a service function from the running deployments
required: true
@@ -722,7 +722,7 @@ paths:
# security:
# - jwt: [ ]
parameters:
- - name: deployedServiceFunctionName
+ - name: app_id
in: path
description: Represents a service function from the running deployments.
required: true
@@ -736,6 +736,137 @@ paths:
"405":
description: Method not allowed
x-openapi-router-controller: swagger_server.controllers.service_functions_instances_controller
+ /sessions:
+ post:
+ tags:
+ - Quality on Demand Functions
+ summary: Creates a new QoD Session
+ operationId: create_qod_session
+ requestBody:
+ description: QoD Session body.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/'
+ responses:
+ "200":
+ description: Session created.
+ x-openapi-router-controller: swagger_server.controllers.network_functions_controller
+ /sessions/{id}:
+ get:
+ tags:
+ - Quality on Demand Functions
+ summary: Retrieve details of a QoD Session
+ operationId: get_qod_session
+ parameters:
+ - name: id
+ in: path
+ description: Represents a QoD Session.
+ required: true
+ style: simple
+ explode: false
+ schema:
+ type: string
+ responses:
+ "200":
+ description: QoD Session found
+ "405":
+ description: Method not allowed
+ "404":
+ description: Session not found
+ x-openapi-router-controller: swagger_server.controllers.network_functions_controller
+ delete:
+ tags:
+ - Quality on Demand Functions
+ summary: Remove QoD Session
+ operationId: delete_qod_session
+ parameters:
+ - name: id
+ in: path
+ description: Represents a QoD Session.
+ required: true
+ style: simple
+ explode: false
+ schema:
+ type: string
+ responses:
+ "201":
+ description: QoD Session deleted
+ "405":
+ description: Method not allowed
+ "404":
+ description: Session not found
+ x-openapi-router-controller: swagger_server.controllers.network_functions_controller
+ /traffic-influences:
+ post:
+ tags:
+ - Traffic Influence Functions
+ summary: Creates a new TrafficInfluence resource
+ operationId: create_traffic_influence_resource
+ requestBody:
+ description: TrafficInfluence body.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/'
+ responses:
+ "200":
+ description: Resource created.
+ x-openapi-router-controller: swagger_server.controllers.network_functions_controller
+ get:
+ tags:
+ - Traffic Influence Functions
+ summary: Retrieves all TrafficInfluence resources
+ operationId: get_all_traffic_influence_resources
+ responses:
+ "200":
+ description: Resources retrieved.
+ x-openapi-router-controller: swagger_server.controllers.network_functions_controller
+ /traffic-influences/{id}:
+ get:
+ tags:
+ - Traffic Influence Functions
+ summary: Retrieve details of a TrafficInfluence resource
+ operationId: get_traffic_influence_resource
+ parameters:
+ - name: id
+ in: path
+ description: Represents a TrafficInfluence resource.
+ required: true
+ style: simple
+ explode: false
+ schema:
+ type: string
+ responses:
+ "200":
+ description: TrafficInfluence resource found
+ "405":
+ description: Method not allowed
+ "404":
+ description: Session not found
+ x-openapi-router-controller: swagger_server.controllers.network_functions_controller
+ delete:
+ tags:
+ - Traffic Influence Functions
+ summary: Remove TrafficInfluence resource
+ operationId: delete_traffic_influence_resource
+ parameters:
+ - name: id
+ in: path
+ description: Represents a TrafficInfluence resource
+ required: true
+ style: simple
+ explode: false
+ schema:
+ type: string
+ responses:
+ "201":
+ description: TrafficInfluence resource deleted
+ "405":
+ description: Method not allowed
+ "404":
+ description: Session not found
+ x-openapi-router-controller: swagger_server.controllers.network_functions_controller
# /securedSlice:
# post:
# tags:
@@ -1546,21 +1677,21 @@ components:
target_port: 8080
published_port: 8080
publish_mode: "host"
- HelmChartInstall:
- type: object
- properties:
- uri:
- type: string
- example: http://helm.chart.net/helm.yaml
- deployment_name:
- type: string
- example: test_helm
- repo_username:
- type: string
- example: test1
- repo_password:
- type: string
- example: test1
+ # HelmChartInstall:
+ # type: object
+ # properties:
+ # uri:
+ # type: string
+ # example: http://helm.chart.net/helm.yaml
+ # deployment_name:
+ # type: string
+ # example: test_helm
+ # repo_username:
+ # type: string
+ # example: test1
+ # repo_password:
+ # type: string
+ # example: test1
CopyArtifactModel:
type: object
properties:
diff --git a/service-resource-manager-implementation/swagger_server/utils/kubernetes_connector.py b/service-resource-manager-implementation/swagger_server/utils/kubernetes_connector.py
index 563f415..0ea2b5e 100644
--- a/service-resource-manager-implementation/swagger_server/utils/kubernetes_connector.py
+++ b/service-resource-manager-implementation/swagger_server/utils/kubernetes_connector.py
@@ -415,7 +415,7 @@ def delete_chain(chain_name):
def deploy_service_function(descriptor_service_function):
#deploys a Deployment yaml file, a service, a pvc and a hpa
# logging.info('DESCRIPTOR: '+descriptor_service_function)
- logging.info(descriptor_service_function)
+ # logging.info(descriptor_service_function)
if "volumes" in descriptor_service_function:
for volume in descriptor_service_function["volumes"]:
#first solution (python k8s client raises error without reason!)
@@ -996,7 +996,7 @@ def get_deployed_service_functions():
#find volumes!
for app_col in apps_col:
- if app_col["required_volumes"] is not None:
+ if app_col.get("required_volumes") is not None:
volumes_=[]
for volume in app_col["required_volumes"]:
for item in api_response_pvc.items:
--
GitLab