Commit 9ba66eb4 authored by Javier Velázquez's avatar Javier Velázquez
Browse files

- Add change scheduler planner and flags for IP and Port

- Add reconifg slice route in swagger namespace
- Add reconfig_slice in api main.py
- Add reconfig_slice in main.py
- Add reconfig action in Realizer
- Improve tests to cover new features
- Correct bugs
parent ed6d7de5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -22,3 +22,4 @@ service.db
telemetry_client.db
alert.db
.python-version
.agents/
+1265 −1246
Original line number Diff line number Diff line
@@ -1244,3 +1244,22 @@ class Api:
            except Exception as e:
                yield f"event: error\ndata: {json.dumps({'error': str(e)})}\n\n"
                break

    def reconfig_slice(self, slice_id):
        """
        Trigger slice reconfiguration via Change Scheduler Planner.

        Args:
            slice_id (str): The identifier of the slice to reconfigure.

        Returns:
            Tuple[dict, int]: Response payload and HTTP status code.
        """
        try:
            result = self.slice_service.reconfig_slice(slice_id)
            return send_response(True, code=200, data=result)
        except ValueError as e:
            return send_response(False, code=404, message=str(e))
        except Exception as e:
            return send_response(False, code=500, message=str(e))
 No newline at end of file
+2 −0
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ def create_config(app: Flask):
    app.config["E2E_OPTICAL_IP"] = os.getenv("E2E_OPTICAL_IP", "127.0.0.1")
    app.config["SUBSCRIBE_ALERTS"] = os.getenv("SUBSCRIBE_ALERTS", "false").lower() == "true"
    app.config["SUBSCRIBE_ALERTS_URL"] = os.getenv("SUBSCRIBE_ALERTS_URL", "")
    app.config["CHANGE_SCHEDULER_IP"] = os.getenv("CHANGE_SCHEDULER_IP", "127.0.0.1")
    app.config["CHANGE_SCHEDULER_PORT"] = int(os.getenv("CHANGE_SCHEDULER_PORT", "8090"))

    # Realizer
    app.config["DUMMY_MODE"] = os.getenv("DUMMY_MODE", "true").lower() == "true"
+73 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ from src.database.service_db import delete_data
from src.mapper.main import mapper
from src.realizer.main import realizer
from src.realizer.send_controller import send_controller
from src.database.sysrepo_store import update_data_store, get_data_store

class NSController:
    """
@@ -227,4 +228,75 @@ class NSController:
        # Request the mapper to aggregate the metrics and store them in the database
        metrics = mapper(payload, action="MONITOR")

        # Check if SLO/SLE compliance indicates non-compliance
        slo_compliance = metrics.get("slo_sle_compliance", {}) if isinstance(metrics, dict) else {}
        is_compliant = slo_compliance.get("is_compliant", True)
        if not is_compliant:
            logging.warning(f"SLO/SLE compliance violation (is_compliant=False) detected for slice '{slice_id}'. Triggering automatic reconfig_slice...")
            try:
                reconfig_res = self.reconfig_slice(slice_id)
                if isinstance(metrics, dict):
                    metrics["reconfig_result"] = reconfig_res
            except Exception as e:
                logging.error(f"Automatic reconfig_slice failed for slice '{slice_id}': {e}")
                if isinstance(metrics, dict):
                    metrics["reconfig_error"] = str(e)

        return metrics

    def reconfig_slice(self, slice_id):
        """
        Reconfigure a network slice by computing the optimal path, comparing it with
        the active service path, and sending scheduled topology changes to Change Scheduler.
        Once Change Scheduler confirms NDT viability, perform slice PUT modification.

        Args:
            slice_id (str): Identifier of the network slice to reconfigure.

        Returns:
            dict: Result from Planner using CHANGE_SCHEDULER strategy and slice modification.
        """
        reconfig_data = realizer({"slice_id": slice_id}, action="RECONFIG", controller_type=self.controller_type)

        from src.planner.planner import Planner
        planner = Planner()
        cs_result = planner.planner(reconfig_data, type="CHANGE_SCHEDULER")

        # Evaluate Change Scheduler response viability
        is_viable = False
        if isinstance(cs_result, dict):
            cs_response = cs_result.get("response", {})
            if cs_result.get("success", True) or cs_result.get("status_code") in (200, 201, 202):
                is_viable = True
            if isinstance(cs_response, dict):
                if cs_response.get("status") in ("VIABLE", "SCHEDULED") or cs_response.get("viable") is True:
                    is_viable = True

        if is_viable:
            logging.info(f"Change Scheduler confirmed viability for slice '{slice_id}'. Performing slice PUT modification...")
            try:
                xpath = f"/ietf-network-slice-service:network-slice-services"
                intent = get_data_store(xpath)
                if not intent:
                    raise ValueError("Network slice services not found")
                if intent:
                    if isinstance(intent, dict) and "network-slice-services" in intent:
                        intent["ietf-network-slice-service:network-slice-services"] = intent.pop("network-slice-services")
                    logging.debug(f"Intent found for slice '{slice_id}': {intent}")
                    mod_result = self.nsc(intent, slice_id=slice_id)

                    if isinstance(cs_result, dict):
                        cs_result["slice_modification"] = mod_result
                    logging.info(f"Slice '{slice_id}' successfully modified via PUT following Change Scheduler viability confirmation.")
                    update_data_store(intent)
                else:
                    logging.warning(f"No stored intent found for slice '{slice_id}' during reconfiguration PUT modification.")
            except Exception as e:
                logging.error(f"Error during slice '{slice_id}' PUT modification: {e}")
                if isinstance(cs_result, dict):
                    cs_result["slice_modification_error"] = str(e)

        return cs_result


+2 −0
Original line number Diff line number Diff line
@@ -133,6 +133,8 @@ def mapper(payload, controller_type="TFS", action="CREATE"):
                    logging.debug(f"Group Template: {template}")
                    
                    connectivity_type = safe_get(connection_group, ['connectivity-type'])
                    if isinstance(connectivity_type, str) and ":" in connectivity_type:
                        connectivity_type = connectivity_type.split(":")[-1]
                    logging.debug(f"Connectivity Type: {connectivity_type}")
                    
                    # Process connectivity constructs
Loading