Skip to content
Commits on Source (6)
from flask import jsonify, request
from pydantic import ValidationError
from edge_cloud_management_api.managers.log_manager import logger
from edge_cloud_management_api.services.pi_edge_services import PiEdgeAPIClientFactory
from edge_cloud_management_api.services.edge_cloud_services import PiEdgeAPIClientFactory
from edge_cloud_management_api.services.storage_service import get_zone
......@@ -99,6 +100,11 @@ def create_app_instance():
if not app_id or not edge_zone_id :
return jsonify({"error": "Missing required fields: appId, edgeCloudZoneId, or kubernetesCLusterRef"}), 400
local_zone = get_zone(edge_zone_id)
if not local_zone:
# TODO: apply federation logic
return 'Zone belongs to federated partner OP. Switching to Federation Manager.'
logger.info(f"Preparing to send deployment request to SRM for appId={app_id}")
pi_edge_client_factory = PiEdgeAPIClientFactory()
......
from flask import jsonify
from pydantic import BaseModel, Field, ValidationError
from typing import List
from edge_cloud_management_api.configs.env_config import config
from edge_cloud_management_api.managers.log_manager import logger
from edge_cloud_management_api.services.pi_edge_services import PiEdgeAPIClientFactory
from edge_cloud_management_api.services.edge_cloud_services import PiEdgeAPIClientFactory
from edge_cloud_management_api.services.storage_service import insert_zones
pi_edge_factory = PiEdgeAPIClientFactory()
api_client = pi_edge_factory.create_pi_edge_api_client()
zones = api_client.edge_cloud_zones()
for zone in zones:
zone['_id'] = zone.get('edgeCloudZoneId')
insert_zones(zones)
class EdgeCloudZone(BaseModel):
edgeCloudZoneId: str = Field(..., description="Unique identifier of the Edge Cloud Zone")
......
from flask import request, jsonify, Response
import json
from flask import request, jsonify
import logging
import connexion
from requests.exceptions import Timeout, ConnectionError
from edge_cloud_management_api.managers.log_manager import logger
import requests
from edge_cloud_management_api.services.federation_services import FederationManagerClientFactory
# Factory pattern
factory = FederationManagerClientFactory()
federation_client = factory.create_federation_client()
def handle_response(status_code, title, detail):
return Response(
json.dumps({
"title": title,
"detail": detail,
"status": status_code
}),
status=status_code,
content_type="application/problem+json"
)
def create_federation():
"""POST /partner - Create federation with partner OP."""
try:
body = request.get_json()
result = federation_client.post_partner(body)
if "error" in result:
return handle_response(502, "Federation Error", result["error"])
return jsonify(result), 200
except Exception as e:
return handle_response(400, "Bad Request", str(e))
body = request.get_json()
token = __get_token()
response = federation_client.post_partner(body, token)
return jsonify(response)
def get_federation(federationContextId):
"""GET /{federationContextId}/partner - Get federation info."""
try:
result = federation_client.get_partner(federationContextId)
if "error" in result:
return handle_response(502, "Federation Error", result["error"])
return jsonify(result), 200
except Exception as e:
return handle_response(400, "Bad Request", str(e))
token = __get_token()
result = federation_client.get_partner(federationContextId, token)
return jsonify(result)
def delete_federation(federationContextId):
"""DELETE /{federationContextId}/partner - Delete federation."""
try:
result = federation_client.delete_partner(federationContextId)
if "error" in result:
return handle_response(502, "Federation Error", result["error"])
return jsonify(result), 200
except Exception as e:
return handle_response(400, "Bad Request", str(e))
token = __get_token()
result = federation_client.delete_partner(federationContextId, token)
return jsonify(result)
def get_federation_context_ids():
"""GET /fed-context-id - Fetch federationContextId(s)."""
try:
result = federation_client.get_federation_context_ids()
if "error" in result:
return handle_response(502, "Federation Error", result["error"])
return jsonify(result), 200
except Exception as e:
return handle_response(400, "Bad Request", str(e))
token = __get_token()
response = federation_client.get_federation_context_ids(token)
return jsonify(response)
def onboard_application_to_partner(federationContextId):
"""POST /{federationContextId}/application/onboarding - Onboard app."""
try:
body = request.get_json()
result = federation_client.onboard_application(federationContextId, body)
if "error" in result:
return handle_response(502, "Onboarding Error", result["error"])
return jsonify(result), 202
except Exception as e:
return handle_response(400, "Bad Request", str(e))
token = __get_token()
result = federation_client.onboard_application(federationContextId, body, token)
return jsonify(result)
def get_onboarded_app(federationContextId, appId):
"""GET /{federationContextId}/application/onboarding/app/{appId}"""
try:
result = federation_client.get_onboarded_app(federationContextId, appId)
if "error" in result:
return handle_response(result.get("status_code", 502), "Retrieval Error", result["error"])
return jsonify(result), 200
except Exception as e:
return handle_response(500, "Internal Server Error", str(e))
token = __get_token()
result = federation_client.get_onboarded_app(federationContextId, appId, token)
return jsonify(result)
def delete_onboarded_app(federationContextId, appId):
"""DELETE /{federationContextId}/application/onboarding/app/{appId}"""
try:
result = federation_client.delete_onboarded_app(federationContextId, appId)
if "error" in result:
return handle_response(result.get("status_code", 502), "Deletion Error", result["error"])
return jsonify({"message": f"App {appId} successfully deleted from partner"}), 200
except Exception as e:
return handle_response(500, "Internal Server Error", str(e))
token = __get_token()
result = federation_client.delete_onboarded_app(federationContextId, appId, token)
return jsonify(result)
def __get_token():
bearer = connexion.request.headers['Authorization']
token = bearer.split()[1]
return token
from flask import jsonify
from pydantic import ValidationError #Field
from edge_cloud_management_api.managers.log_manager import logger
from edge_cloud_management_api.services.pi_edge_services import PiEdgeAPIClientFactory
from edge_cloud_management_api.services.edge_cloud_services import PiEdgeAPIClientFactory
def create_qod_session(body: dict):
"""
......
import os
from jose import JWTError, jwt
from werkzeug.exceptions import Unauthorized
def decode_token(token:str):
JWT_ISSUER = os.environ.get('JWT_ISSUER')
PUBLIC_KEY = os.environ.get('JWT_PUBLIC_KEY')
try:
return jwt.decode(token, key=PUBLIC_KEY, algorithms=["RS256"], issuer=JWT_ISSUER)
except JWTError as e:
raise Unauthorized from e
def check_oAuth2ClientCredentials(token):
return {'scopes': ['fed-mgmt'], 'uid': 'test_value'}
def validate_scope_oAuth2ClientCredentials(required_scopes, token_scopes):
return set(required_scopes).issubset(set(token_scopes))
......@@ -9,14 +9,12 @@ proxies = {
"https": config.HTTP_PROXY,
}
class PiEdgeAPIClient:
def __init__(self, base_url, username, password):
self.base_url = base_url
self.username = username
self.password = password
self.token = None
#self.requests_session = self._get_proxy_session(proxies)
def _get_proxy_session(self, session_proxies):
......@@ -340,9 +338,6 @@ class PiEdgeAPIClient:
logger.info(e.args)
return e.args
class PiEdgeAPIClientFactory:
"""
Factory class to create instances of PiEdgeAPIClient.
......
......@@ -3,27 +3,30 @@ import requests
from requests.exceptions import Timeout, ConnectionError
from edge_cloud_management_api.configs.env_config import config
from edge_cloud_management_api.managers.log_manager import logger
from edge_cloud_management_api.services.pi_edge_services import PiEdgeAPIClientFactory
from edge_cloud_management_api.services.edge_cloud_services import PiEdgeAPIClientFactory
class FederationManagerClient:
def __init__(self, base_url=None):
self.base_url = base_url or config.FEDERATION_MANAGER_HOST
def _get_headers(self):
return {
"Content-Type": "application/json",
"Accept": "application/json"
}
def _get_headers(self, token):
headers = {}
if token is not None:
headers['Authorization'] = 'Bearer '+token
headers['Content-Type'] = 'application/json'
headers['Accept'] = 'application/json'
return headers
def post_partner(self, data: dict):
def post_partner(self, data: dict, token: str):
url = f"{self.base_url}/partner"
try:
response = requests.post(url, json=data, headers=self._get_headers(), timeout=10)
response = requests.post(url, json=data, headers=self._get_headers(token), timeout=10)
response.raise_for_status()
return response.json()
except Timeout:
logger.error("POST /partner timed out")
return {"error": "Request timed out"}
return {"error": "Request timed out"}, 408
except ConnectionError:
logger.error("POST /partner connection error")
return {"error": "Connection error"}
......@@ -34,10 +37,10 @@ class FederationManagerClient:
logger.error(f"POST /partner unexpected error: {e}")
return {"error": str(e)}
def get_partner(self, federation_context_id: str):
def get_partner(self, federation_context_id: str, token: str):
url = f"{self.base_url}/{federation_context_id}/partner"
try:
response = requests.get(url, headers=self._get_headers(), timeout=10)
response = requests.get(url, headers=self._get_headers(token), timeout=10)
response.raise_for_status()
return response.json()
except Timeout:
......@@ -53,10 +56,10 @@ class FederationManagerClient:
logger.error(f"GET /{id}/partner unexpected error: {e}")
return {"error": str(e)}
def delete_partner(self, federation_context_id: str):
def delete_partner(self, federation_context_id: str, token: str):
url = f"{self.base_url}/{federation_context_id}/partner"
try:
response = requests.delete(url, headers=self._get_headers(), timeout=10)
response = requests.delete(url, headers=self._get_headers(token), timeout=10)
if response.content:
return response.json()
return {"status": response.status_code}
......@@ -73,10 +76,10 @@ class FederationManagerClient:
logger.error(f"DELETE /{id}/partner unexpected error: {e}")
return {"error": str(e)}
def get_federation_context_ids(self):
def get_federation_context_ids(self, token: str):
url = f"{self.base_url}/fed-context-id"
try:
response = requests.get(url, headers=self._get_headers(), timeout=10)
response = requests.get(url, headers=self._get_headers(token), timeout=10)
response.raise_for_status()
return response.json()
except Timeout:
......@@ -87,12 +90,13 @@ class FederationManagerClient:
return {"error": "Connection error"}
except requests.exceptions.HTTPError as http_err:
logger.error(f"GET /fed-context-id HTTP error: {http_err}")
return {"error": str(http_err), "status_code": response.status_code}
if response.status_code==404:
return {'erorr': http_err}, 404
except Exception as e:
logger.error(f"GET /fed-context-id unexpected error: {e}")
return {"error": str(e)}
def onboard_application(self, federation_context_id: str, body: dict):
def onboard_application(self, federation_context_id: str, body: dict, token: str):
url = f"{self.base_url}/{federation_context_id}/application/onboarding"
try:
response = requests.post(url, headers=self._get_headers(), json=body, timeout=10)
......@@ -112,10 +116,10 @@ class FederationManagerClient:
return {"error": str(e)}
def get_onboarded_app(self, federation_context_id: str, app_id: str):
def get_onboarded_app(self, federation_context_id: str, app_id: str, token: str):
url = f"{self.base_url}/{federation_context_id}/application/onboarding/app/{app_id}"
try:
response = requests.get(url, headers=self._get_headers(), timeout=10)
response = requests.get(url, headers=self._get_headers(token), timeout=10)
response.raise_for_status()
return response.json()
except Timeout:
......@@ -131,7 +135,7 @@ class FederationManagerClient:
logger.error(f"GET onboarded app unexpected error: {e}")
return {"error": str(e), "status_code": 500}
def delete_onboarded_app(self, federation_context_id: str, app_id: str):
def delete_onboarded_app(self, federation_context_id: str, app_id: str, token: str):
url = f"{self.base_url}/{federation_context_id}/application/onboarding/app/{app_id}"
try:
response = requests.delete(url, headers=self._get_headers(), timeout=10)
......
from edge_cloud_management_api.configs.env_config import config
import pymongo
storage_url = mongo_host = config.MONGO_URI
mydb_mongo = 'oeg_storage'
def insert_zones(zone_list: list):
collection = "zones"
myclient = pymongo.MongoClient(storage_url)
mydbmongo = myclient[mydb_mongo]
col = mydbmongo[collection]
col.insert_many(zone_list)
def get_zone(zone_id: str):
collection = "zones"
myclient = pymongo.MongoClient(storage_url)
mydbmongo = myclient[mydb_mongo]
col = mydbmongo[collection]
zone = col.find_one({'_id': zone_id})
return zone
\ No newline at end of file
---
openapi: 3.0.3
info:
title: Edge Application Management API
version: 0.9.3-wip
title: Open Exposure Gateway API
version: 1.0.1-wip
description: |
Edge Application Management API allows API consumers to manage the
Life Cycle of an Application and to Discover Edge Cloud Zones.
Open Exposure Gateway API allows API consumers to manage the
Life Cycle of an Application, Discover Edge Cloud Zones and request Network Resources.
# Overview
The reference scenario foresees a distributed Telco Edge Cloud where any
Application Delevoper, known as an Application Provider, can host and
......@@ -149,20 +149,12 @@ info:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
externalDocs:
description: Product documentation at Camara
description: Product documentation at Camara0
url: https://github.com/camaraproject/EdgeCloud
servers:
- url: http://vitrualserver:8080/oeg/1.0.0
tags:
- name: Application
description: Application and Application Instance Lice Cycle Management
- name: Edge Cloud
description: Edge Cloud Zones Availability
- name: FederationManagement
description: Federation Manager
paths:
/apps:
post:
......@@ -817,17 +809,16 @@ paths:
"405":
description: Method not allowed
"404":
<<<<<<< HEAD
description: Session not found
=======
description: Session not found
>>>>>>> f18e931881554973db7ae6241588db6e667dbf10
/partner:
post:
tags:
- FederationManagement
summary: Creates one direction federation with partner operator platform.
security:
- oAuth2ClientCredentials:
- fed-mgmt
operationId: edge_cloud_management_api.controllers.federation_manager_controller.create_federation
requestBody:
content:
......@@ -876,14 +867,14 @@ paths:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"409":
description: Conflict
"408":
description: Timeout
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"422":
description: Unprocessable Entity
"409":
description: Conflict
content:
application/problem+json:
schema:
......@@ -894,20 +885,6 @@ paths:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"503":
description: Service Unavailable
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"520":
description: Web Server Returned an Unknown Error
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
default:
description: Generic Error
callbacks:
onPartnerStatusEvent:
'{$request.body#/partnerStatusLink }':
......@@ -1069,6 +1046,9 @@ paths:
\ The response shall provide info about the zones offered by the partner,\
\ partner OP network codes, information about edge discovery and LCM service\
\ etc."
security:
- oAuth2ClientCredentials:
- fed-mgmt
operationId: edge_cloud_management_api.controllers.federation_manager_controller.get_federation
parameters:
- name: federationContextId
......@@ -1085,12 +1065,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/inline_response_200_1'
"400":
description: Bad request
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"401":
description: Unauthorized
content:
......@@ -1103,42 +1077,19 @@ paths:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"409":
description: Conflict
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"422":
description: Unprocessable Entity
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"500":
description: Internal Server Error
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"503":
description: Service Unavailable
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"520":
description: Web Server Returned an Unknown Error
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
default:
description: Generic Error
delete:
tags:
- FederationManagement
summary: Remove existing federation with the partner OP
security:
- oAuth2ClientCredentials:
- fed-mgmt
operationId: edge_cloud_management_api.controllers.federation_manager_controller.delete_federation
parameters:
- name: federationContextId
......@@ -1151,27 +1102,14 @@ paths:
responses:
"200":
description: Federation removed successfully
"400":
description: Bad request
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"401":
description: Unauthorized
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"409":
description: Conflict
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"422":
description: Unprocessable Entity
"404":
description: Not Found
content:
application/problem+json:
schema:
......@@ -1182,25 +1120,14 @@ paths:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"503":
description: Service Unavailable
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
"520":
description: Web Server Returned an Uknown Error
content:
application/problem+json:
schema:
$ref: '#/components/schemas/ProblemDetails'
default:
description: Generic Error
/fed-context-id:
get:
tags:
- FederationManagement
summary: Retrieves the existing federationContextId with partner operator platform.
security:
- oAuth2ClientCredentials:
- fed-mgmt
operationId: edge_cloud_management_api.controllers.federation_manager_controller.get_federation_context_ids
responses:
"200":
......@@ -1245,10 +1172,22 @@ paths:
components:
# securitySchemes:
# openId:
# description: OpenID Provider Configuration Information.
# type: openIdConnect
# openIdConnectUrl: https://example.com/.well-known/openid-configuration
# jwt: # arbitrary name for the security scheme
# type: http
# scheme: bearer
# bearerFormat: JWT
# x-bearerInfoFunc: edge_cloud_management_api.controllers.security_controller.decode_token
securitySchemes:
oAuth2ClientCredentials:
type: oauth2
flows:
clientCredentials:
tokenUrl: http://isiath.duckdns.org:8081//realms/federation/protocol/openid-connect/token
scopes:
fed-mgmt: Access to the federation APIs
x-tokenInfoFunc: edge_cloud_management_api.controllers.security_controller.check_oAuth2ClientCredentials
x-scopeValidateFunc: edge_cloud_management_api.controllers.security_controller.validate_scope_oAuth2ClientCredentials
parameters:
x-correlator:
name: x-correlator
......
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.26.0 (40646f47)
creationTimestamp: null
labels:
io.kompose.service: oegmongo
name: oegmongo
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: oegmongo
strategy:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.26.0 (40646f47)
creationTimestamp: null
labels:
#io.kompose.network/netEMPkub: "true"
io.kompose.service: oegmongo
spec:
containers:
- image: mongo
name: oegmongo
ports:
- containerPort: 27017
resources: {}
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.26.0 (40646f47)
creationTimestamp: null
labels:
io.kompose.service: oegmongo
name: oegmongo
spec:
type: ClusterIP
ports:
- name: "27018"
port: 27018
targetPort: 27017
selector:
io.kompose.service: oegmongo
status:
loadBalancer: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.26.0 (40646f47)
creationTimestamp: null
labels:
io.kompose.service: oegcontroller
name: oegcontroller
spec:
replicas: 1
selector:
matchLabels:
io.kompose.service: oegcontroller
strategy: {}
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.26.0 (40646f47)
creationTimestamp: null
labels:
io.kompose.service: oegcontroller
spec:
containers:
- env:
- name: MONGO_URI
value: mongodb://oegmongo/sample_db?authSource=admin
- name: SRM_HOST
value: http://srm:8080/srm/1.0.0
- name: FEDERATION_MANAGER_HOST
value: http://federation-manager:8989
image: ghcr.io/sunriseopenoperatorplatform/oeg/oeg
name: oegcontroller
ports:
- containerPort: 8080
resources: {}
imagePullPolicy: Always
restartPolicy: Always
status: {}
---
apiVersion: v1
kind: Service
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.26.0 (40646f47)
creationTimestamp: null
labels:
io.kompose.service: oeg
name: oeg
spec:
type: NodePort
ports:
- name: "8080"
nodePort: 32414
port: 8080
targetPort: 8080
selector:
io.kompose.service: oegcontroller
status:
loadBalancer: {}
This diff is collapsed.