Commit c2b80bab authored by George Papathanail's avatar George Papathanail
Browse files

artefact and onboarding changes

parent bc177362
Loading
Loading
Loading
Loading
+339 −23
Original line number Diff line number Diff line
@@ -139,36 +139,27 @@ def create_app_instance():

            artefact = {
                "artefactId": artefact_id,
                "artefactName": artefact_id,
                "appProviderId": appData.get("appProvider"),
                "artefactVersionInfo": "22.3.9",
                "artefactDescription": appData.get("description", ""),

                "artefactName": "library/nginx",
                "artefactVersionInfo": "latest",
                "artefactVirtType": "CONTAINER_TYPE",
                "artefactDescriptorType": "HELM",
                "artefactDescriptorTypeR": "HELM",
                "artefactFileName": f"{appData.get('name')}.tar",
                "artefactFileFormat": "TAR",
                "artefactDescriptorType": "COMPONENTSPEC",

                "repoType": "PUBLICREPO",
                "artefactRepoLocation": {
                    "repoURL": "https://charts.bitnami.com/bitnami",
                    "userName": "",
                    "password": "",
                    "token": ""
                    "repoURL": "docker.io"
                },

                "componentSpec": [
                    {
                        "componentName": "nginx",
                        "images": ["bitnami/nginx:1.29.4"],
                        "images": ["nginx:latest"],
                        "numOfInstances": 1,
                        "restartPolicy": "RESTART_POLICY_ALWAYS",
                        "computeResourceProfile": {
                            "cpuArchType": "ISA_X86_64",
                            "numCPU": "1",
                            "memory": 1024,
                            "diskStorage": 10240
                            "numCPU": "100m",
                            "memory": 128
                        }
                    }
                ]
@@ -182,11 +173,18 @@ def create_app_instance():
            print(json.dumps(artefact, indent=2))
            print("================================================\n")

            # ============================================================
            # Step 3: Create artefact at Federation Manager
            # ============================================================
            artefact_body, artefact_status = federation_client.create_artefact(
                artefact=artefact,
                federation_context_id=zone.get("fedContextId"),
                token=fed_token
            )
            print(f"\n========== ARTEFACT CREATION RESPONSE ==========")
            print(f"Status: {artefact_status}")
            print(f"Response: {json.dumps(artefact_body, indent=2)}")
            print("================================================\n")

            # Idempotency: duplicate artefact = success
            if artefact_status == 422 and "duplicate key" in str(artefact_body):
@@ -199,6 +197,282 @@ def create_app_instance():
                    "fm_response": artefact_body
                }), artefact_status

            # ============================================================
            # Step 4: Onboard application at partner OP (HARDCODED FOR TESTING)
            # ============================================================
            fed_context_id = zone.get("fedContextId")
            logger.info(f"Federation Context ID from zone: {fed_context_id}")
            logger.info(f"Federation Manager URL: {federation_client.base_url}")
            print(f"\n========== FEDERATION CONTEXT DEBUG ==========")
            print(f"Using Federation Context ID: {fed_context_id}")
            print(f"Federation Manager Base URL: {federation_client.base_url}")
            print(f"Full Onboard URL: {federation_client.base_url}/{fed_context_id}/application/onboarding")
            print("===============================================\n")
            print("\n========== CHECKING FEDERATION IDS ==========")
            fed_ids_body, fed_ids_status = federation_client.get_federation_context_ids(token=fed_token)
            print(f"Status: {fed_ids_status}")
            print(f"Federation IDs: {json.dumps(fed_ids_body, indent=2)}")
            print(f"Looking for: {zone.get('fedContextId')}")
            print("=============================================\n")
            print(f"\n========== HEADERS DEBUG ==========")
            print(f"X-Partner-API-Root: {federation_client.partner_root}")
            print(f"Authorization: Bearer {fed_token[:20]}...")
            print("===================================\n")
            print ("DEBUG: About to check partner status...")
            try:
                print("\n========== CHECKING PARTNER STATUS ==========")
                partner_body, partner_status = federation_client.get_partner(
                    federation_context_id=zone.get("fedContextId"),
                    token=fed_token
                )
                print(f"Partner Status: {partner_status}")
                print(f"Partner Info: {json.dumps(partner_body, indent=2)}")
                print("=============================================\n")
            except Exception as e:
                print(f"\n========== PARTNER CHECK ERROR ==========")
                print(f"Error getting partner status: {str(e)}")
                print("=========================================\n")


            #print("\n========== VERIFYING FEDERATION ==========")
            #fed_verify_body, fed_verify_status = federation_client.get_federation(
            #    federation_context_id=zone.get("fedContextId"),
             #   token=fed_token
           # )
           # print(f"Federation Verification Status: {fed_verify_status}")
           # print(f"Federation Verification Response: {json.dumps(fed_verify_body, indent=2)}")
           # print("==========================================\n")

            #if fed_verify_status != 200:
            #    return jsonify({
              #      "error": "Federation context not found or not accessible",
              #      "federation_context_id": zone.get("fedContextId"),
              #      "verification_response": fed_verify_body
              #  }), fed_verify_status
            onboard_app = {
                "appId": "Zb8uaZ9vS5x",
                "appProviderId": "p7y0UqLRn0oMBWxLqxsZJEjfkA0JmHVW5",
                "appMetaData": {
                    "appName": "ofPZpa02jYqebqPTgypFrH87jS3Kr",
                    "version": "v1",
                    "accessToken": "EpIhgCRuL83lyy7dOLQOcIOCSYHPOdSgwKk8l"
                },
                "appQoSProfile": {
                    "latencyConstraints": "NONE"
                },
                "appComponentSpecs": [
                    {
                        "artefactId": "dddd4444-eeee-5555-ffff-666666666666"
                    }
                ],
                "appStatusCallbackLink": ""
            }

            print("\n========== OEG → FM ONBOARD PAYLOAD ==========")
            print(json.dumps(onboard_app, indent=2))
            print("==============================================\n")



            onboard_app_body, onboard_app_status = federation_client.onboard_application(
                federation_context_id=zone.get("fedContextId"),
                body=onboard_app,
                token=fed_token
            )
            print(f"\n========== ONBOARD RESPONSE DEBUG ==========")
            print(f"Status: {onboard_app_status}")
            print(f"Response Body: {json.dumps(onboard_app_body, indent=2)}")
            print("============================================\n")

            if onboard_app_status != 200:
                return jsonify({
                    "error": "Application onboarding failed",
                    "fm_response": onboard_app_body
                }), onboard_app_status



            # ============================================================
            # Step 5: Deploy application at partner OP (HARDCODED FOR TESTING)
            # ============================================================
            deploy_app = {
                "appId": "Zb8uaZ9vS5x",
                "appProviderId": "p7y0UqLRn0oMBWxLqxsZJEjfkA0JmHVW5",
                "appVersion": "v1",
                "appInstCallbackLink": "",
                "zoneInfo": {
                    "zoneId": zone.get("edgeCloudZoneId"),
                    "flavourId": "string",
                    "resPool": zone.get("resPool", "string"),
                    "resourceConsumption": "RESERVED_RES_AVOID"
                }
            }

            print("\n========== OEG → FM DEPLOY PAYLOAD ==========")
            print(json.dumps(deploy_app, indent=2))
            print("=============================================\n")

            deploy_app_body, deploy_app_status = federation_client.deploy_app_partner(
                federation_context_id=zone.get("fedContextId"),
                body=deploy_app,
                token=fed_token
            )

            if deploy_app_status in (200, 201, 202):
                return jsonify({
                    "message": "Application deployed successfully at partner OP",
                    "appId": app_id,
                    "deployment_response": deploy_app_body
                }), deploy_app_status
            else:
                return jsonify({
                    "error": "Application deployment failed",
                    "fm_response": deploy_app_body
                }), deploy_app_status

        # ============================================================
        # LOCAL DEPLOYMENT (SRM path)
        # ============================================================
        logger.info(f"Proceeding with LOCAL deployment for appId={app_id}")

        try:
            logger.debug("Sending deployment request to SRM")
            response = pi_edge_client.deploy_service_function(data=body)

            if isinstance(response, dict) and "error" in response:
                logger.warning(
                    "SRM returned an error, deployment not completed"
                )
                return jsonify({
                    "warning": "Deployment request accepted but not completed",
                    "details": response
                }), 202

            logger.info("Local deployment request successfully sent to SRM")
            return jsonify({
                "message": "Application deployed locally",
                "appId": app_id,
                "response": response
            }), 202

        except Exception as e:
            logger.error(f"SRM deployment failed: {str(e)}")
            return jsonify({
                "warning": "SRM backend unavailable",
                "details": str(e)
            }), 202

    except Exception as e:
        logger.exception("Unexpected error in create_app_instance")
        return jsonify({
            "error": "Unexpected error",
            "details": str(e)
        }), 500
'''
 def create_app_instance():


    logger.info("Received request to create app instance")

    try:
        body = request.get_json()
        logger.debug(f"Request body: {body}")

        app_id = body.get("appId")
        app_zones = body.get("appZones")

        if not app_id or not app_zones:
            return jsonify({
                "error": "Missing required fields: appId, appZones"
            }), 400

        pi_edge_client_factory = PiEdgeAPIClientFactory()
        pi_edge_client = pi_edge_client_factory.create_pi_edge_api_client()

        zone = get_zone(
            app_zones[0]
            .get("EdgeCloudZone", {})
            .get("edgeCloudZoneId")
        )

        # ============================================================
        # PARTNER DEPLOYMENT (Federation path)
        # ============================================================
        if zone.get("isLocal") == "false":

            # ============================================================
            # Step 1: Retrieve application metadata from SRM
            # ============================================================
            app_response = pi_edge_client.get_app(appId=app_id)
            appData = app_response.get("appManifest")

            if not appData:
                return jsonify({
                    "error": "Application manifest not found",
                    "appId": app_id
                }), 404

            # ============================================================
            # Step 2: Compose GSMA artefact payload
            # artefactId == appId (INTENTIONAL)
            # ============================================================
            artefact_id = app_id

            artefact = {
                "artefactId": artefact_id,
                "appProviderId": appData.get("appProvider"),
                "artefactName": "library/nginx",
                "artefactVersionInfo": "latest",
                "artefactVirtType": "CONTAINER_TYPE",
                "artefactDescriptorType": "COMPONENTSPEC",

                "repoType": "PUBLICREPO",
                "artefactRepoLocation": {
                    "repoURL": "docker.io"
                },

                "componentSpec": [
                    {
                        "componentName": "nginx",
                        "images": ["nginx:latest"],
                        "numOfInstances": 1,
                        "restartPolicy": "RESTART_POLICY_ALWAYS",
                        "computeResourceProfile": {
                            "cpuArchType": "ISA_X86_64",
                            "numCPU": "100m",
                            "memory": 128
                        }
                    }
                ]
            }

            fed_token = get_fed(
                zone.get("fedContextId")
            ).get("token")

            print("\n========== OEG → FM ARTEFACT PAYLOAD ==========")
            print(json.dumps(artefact, indent=2))
            print("================================================\n")

            # ============================================================
            # Step 3: Create artefact at Federation Manager
            # ============================================================
            artefact_body, artefact_status = federation_client.create_artefact(
                artefact=artefact,
                federation_context_id=zone.get("fedContextId"),
                token=fed_token
            )

            # Idempotency: duplicate artefact = success
            if artefact_status == 422 and "duplicate key" in str(artefact_body):
                logger.info("Artefact already exists in FM, continuing")
                artefact_status = 200

            if artefact_status not in (200, 409):
                return jsonify({
                    "error": "Artefact creation failed",
                    "fm_response": artefact_body
                }), artefact_status

            # ============================================================
            # Step 4: Onboard application at partner OP
@@ -245,17 +519,59 @@ def create_app_instance():
                "appStatusCallbackLink": "http://oeg/api/status"
            }

            federation_client.onboard_application(
            print("\n========== OEG → FM ONBOARD PAYLOAD ==========")
            print(json.dumps(onboard_app, indent=2))
            print("==============================================\n")

            onboard_app_body, onboard_app_status = federation_client.onboard_application(
                federation_context_id=zone.get("fedContextId"),
                body=onboard_app,
                token=fed_token
            )

            # Step 5 intentionally skipped
            if onboard_app_status != 200:
                return jsonify({
                "message": "Application onboarded successfully (partner OP)",
                "appId": app_id
            }), 202
                    "error": "Application onboarding failed",
                    "fm_response": onboard_app_body
                }), onboard_app_status

            # ============================================================
            # Step 5: Deploy application at partner OP
            # ============================================================
            deploy_app = {
                "appId": app_id,
                "appVersion": appData.get("version") or "1.0.0",
                "appProviderId": appData.get("appProvider"),
                "appInstCallbackLink": "http://oeg/api/status",
                "zoneInfo": {
                    "zoneId": zone.get("edgeCloudZoneId"),
                    "flavourId": "string",
                    "resPool": zone.get("resPool", "string"),
                    "resourceConsumption": "RESERVED_RES_AVOID"
                }
            }

            print("\n========== OEG → FM DEPLOYMENT PAYLOAD ==========")
            print(json.dumps(deploy_app, indent=2))
            print("==================================================\n")

            deploy_app_body, deploy_app_status = federation_client.deploy_app_partner(
                federation_context_id=zone.get("fedContextId"),
                body=deploy_app,
                token=fed_token
            )

            if deploy_app_status in (200, 201, 202):
                return jsonify({
                    "message": "Application deployed successfully at partner OP",
                    "appId": app_id,
                    "deployment_response": deploy_app_body
                }), deploy_app_status
            else:
                return jsonify({
                    "error": "Application deployment failed",
                    "fm_response": deploy_app_body
                }), deploy_app_status

        # ============================================================
        # LOCAL DEPLOYMENT (SRM path)
@@ -296,7 +612,7 @@ def create_app_instance():
            "details": str(e)
        }), 500


'''
def get_app_instance(app_id=None, x_correlator=None, app_instance_id=None, region=None):
    """
    Retrieve application instances from the database.
+68 −16
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ class FederationManagerClient:
            logger.error(f"DELETE /{id}/partner unexpected error: {e}")
            return {"error": str(e)}, 500


    def get_federation_context_ids(self, token: str):
        url = f"{self.base_url}/fed-context-id"
        try:
@@ -106,26 +107,62 @@ class FederationManagerClient:
            logger.error(f"GET /fed-context-id unexpected error: {e}")
            return {"error": str(e)}, 500

    def get_federation(self, federation_context_id: str, token: str):
        """Verify that a federation context exists"""
        url = f"{self.base_url}/{federation_context_id}"
        try:
            response = requests.get(
                url,
                headers=self._get_headers(token),
                timeout=10
            )
            try:
                response_body = response.json()
            except ValueError:
                response_body = response.text
            return response_body, response.status_code
        except Timeout:
            logger.error(f"GET /{federation_context_id} timed out")
            return {"error": "Request timed out"}, 408
        except ConnectionError:
            logger.error(f"GET /{federation_context_id} connection error")
            return {"error": "Connection error"}, 503
        except requests.exceptions.HTTPError as http_err:
            logger.error(f"GET /{federation_context_id} HTTP error: {http_err}")
            return {"error": str(http_err)}, response.status_code
        except Exception as e:
            logger.error(f"GET /{federation_context_id} unexpected error: {e}")
            return {"error": str(e)}, 500

        
    '''---PARTNER APP ONBOARDING---'''

    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(token), json=body, timeout=10)
           response.raise_for_status()
           return response.json()
            response = requests.post(
                url,
                headers=self._get_headers(token),
                json=body,
                timeout=10
            )
            try:
                response_body = response.json()
            except ValueError:
                response_body = response.text
            return response_body, response.status_code
        except Timeout:
            logger.error("POST /application/onboarding timed out")
          return {"error": "Request timed out"}
            return {"error": "Request timed out"}, 408
        except ConnectionError:
            logger.error("POST /application/onboarding connection error")
            return {"error": "Connection error"}
            return {"error": "Connection error"}, 503
        except requests.exceptions.HTTPError as http_err:
            logger.error(f"POST /application/onboarding HTTP error: {http_err}")
            return {"error": str(http_err), "status_code": response.status_code}
            return {"error": str(http_err)}, response.status_code
        except Exception as e:
            logger.error(f"POST /application/onboarding unexpected error: {e}")
            return {"error": str(e)}
            return {"error": str(e)}, 500


    def get_onboarded_app(self, federation_context_id: str, app_id: str, token: str):
@@ -171,11 +208,29 @@ class FederationManagerClient:
    def deploy_app_partner(self, federation_context_id: str, body: dict, token: str):
        url = f"{self.base_url}/{federation_context_id}/application/lcm"
        try:
            response = requests.post(url, headers=self._get_headers(token), json=body, timeout=10)
            return response
            response = requests.post(
                url,
                headers=self._get_headers(token),
                json=body,
                timeout=10
            )
            try:
                response_body = response.json()
            except ValueError:
                response_body = response.text
            return response_body, response.status_code
        except Timeout:
            logger.error("POST /application/lcm timed out")
            return {"error": "Request timed out"}, 408
        except ConnectionError:
            logger.error("POST /application/lcm connection error")
            return {"error": "Connection error"}, 503
        except requests.exceptions.HTTPError as http_err:
            logger.error(f"POST /application/lcm HTTP error: {http_err}")
            return {"error": str(http_err)}, response.status_code
        except Exception as e:
            logger.error(f"DELETE onboarding app unexpected error: {e}")
            return {"error": str(e), "status_code": 500}
            logger.error(f"POST /application/lcm unexpected error: {e}")
            return {"error": str(e)}, 500

    '''---AVAILABILITY ZONE INFO SYNCHRONIZATION---'''

@@ -247,14 +302,11 @@ class FederationManagerClient:
                json=artefact,
                timeout=120
            )

            try:
                body = response.json()
            except ValueError:
                body = response.text

            return body, response.status_code

        except Exception as e:
            logger.error(f"Create artefact unexpected error: {e}")
            return {"error": str(e)}, 500