Loading src/api/main.py 0 → 100644 +193 −0 Viewed Changes for src/api/main.py: 193 added lines, 0 removed lines. Original line number Diff line number Diff line from src.config.constants import DATABASE_PATH from src.utils.send_response import send_response import os, json, logging from flask import current_app class Api: def __init__(self, slice_service): self.slice_service = slice_service def add_flow(self, intent): """ Create a new transport network slice. Args: intent (dict): Network slice intent in 3GPP or IETF format Returns: Result of the Network Slice Controller (NSC) operation API Endpoint: POST /slice Raises: ValueError: If no transport network slices are found Exception: For unexpected errors during slice creation process """ try: result = self.slice_service.nsc(intent) if not result: return send_response(False, code=404, message="No intents found") return send_response( True, code=201, data=result ) except Exception as e: # Handle unexpected errors return send_response(False, code=500, message=str(e)) def get_flows(self,slice_id=None): """ Retrieve transport network slice information. This method allows retrieving: - All transport network slices - A specific slice by its ID Args: slice_id (str, optional): Unique identifier of a specific slice. Defaults to None. Returns: dict or list: - If slice_id is provided: Returns the specific slice details - If slice_id is None: Returns a list of all slices - Returns an error response if no slices are found API Endpoint: GET /slice/{id} Raises: ValueError: If no transport network slices are found Exception: For unexpected errors during file processing """ try: # Read slice database from JSON file with open(os.path.join(DATABASE_PATH, "slice_ddbb.json"), 'r') as file: content = json.load(file) # If specific slice ID is provided, find and return matching slice if slice_id: for slice in content: if slice["slice_id"] == slice_id: return slice, 200 raise ValueError("Transport network slices not found") # If no slices exist, raise an error if len(content) == 0: raise ValueError("Transport network slices not found") # Return all slices if no specific ID is given return [slice for slice in content if slice.get("controller") == self.slice_service.controller_type], 200 except ValueError as e: # Handle case where no slices are found return send_response(False, code=404, message=str(e)) except Exception as e: # Handle unexpected errors return send_response(False, code=500, message=str(e)) def modify_flow(self,slice_id, intent): """ Modify an existing transport network slice. Args: slice_id (str): Unique identifier of the slice to modify intent (dict): New intent configuration for the slice Returns: Result of the Network Slice Controller (NSC) operation API Endpoint: PUT /slice/{id} """ try: result = self.slice_service.nsc(intent, slice_id) if not result: return send_response(False, code=404, message="Slice not found") return send_response( True, code=200, message="Slice modified successfully", data=result ) except Exception as e: # Handle unexpected errors return send_response(False, code=500, message=str(e)) def delete_flows(self, slice_id=None): """ Delete transport network slice(s). This method supports: - Deleting a specific slice by ID - Deleting all slices - Optional cleanup of L2VPN configurations Args: slice_id (str, optional): Unique identifier of slice to delete. Defaults to None. Returns: dict: Response indicating successful deletion or error details API Endpoint: DELETE /slice/{id} Raises: ValueError: If no slices are found to delete Exception: For unexpected errors during deletion process Notes: - If controller_type is TFS, attempts to delete from Teraflow - If need_l2vpn_support is True, performs additional L2VPN cleanup """ try: # Read current slice database with open(os.path.join(DATABASE_PATH, "slice_ddbb.json"), 'r') as file: content = json.load(file) id = None # Delete specific slice if slice_id is provided if slice_id: for i, slice in enumerate(content): if slice["slice_id"] == slice_id and slice.get("controller") == self.slice_service.controller_type: del content[i] id = i break # Raise error if slice not found if id is None: raise ValueError("Transport network slice not found") # Update slice database with open(os.path.join(DATABASE_PATH, "slice_ddbb.json"), 'w') as file: json.dump(content, file, indent=4) logging.info(f"Slice {slice_id} removed successfully") return {}, 204 # Delete all slices else: # Optional: Delete in Teraflow if configured if self.slice_service.controller_type == "TFS": # TODO: should send a delete request to Teraflow if current_app.config["TFS_L2VPN_SUPPORT"]: self.slice_service.tfs_l2vpn_delete() data_removed = [slice for slice in content if slice.get("controller") == self.slice_service.controller_type] # Verify slices exist before deletion if len(data_removed) == 0: raise ValueError("Transport network slices not found") filtered_data = [slice for slice in content if slice.get("controller") != self.slice_service.controller_type] # Clear slice database with open(os.path.join(DATABASE_PATH, "slice_ddbb.json"), 'w') as file: json.dump(filtered_data, file, indent=4) logging.info("All slices removed successfully") return {}, 204 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 src/config/.env.example 0 → 100644 +39 −0 Viewed Changes for src/config/.env.example: 39 added lines, 0 removed lines. Original line number Diff line number Diff line # ------------------------- # General # ------------------------- LOGGING_LEVEL=INFO # Options: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET DUMP_TEMPLATES=false # ------------------------- # Mapper # ------------------------- # Flag to determine if the NSC performs NRPs NRP_ENABLED=false # Planner Flags PLANNER_ENABLED=true # Flag to determine if external PCE is used PCE_EXTERNAL=false # ------------------------- # Realizer # ------------------------- # If true, no config sent to controllers DUMMY_MODE=true # ------------------------- # Teraflow # ------------------------- TFS_IP=127.0.0.1 UPLOAD_TYPE=WEBUI # Options: WEBUI o NBI # Flag to determine if additional L2VPN configuration support is required for deploying L2VPNs with path selection TFS_L2VPN_SUPPORT=false # ------------------------- # IXIA # ------------------------- IXIA_IP=127.0.0.1 # ------------------------- # WebUI # ------------------------- WEBUI_DEPLOY=true src/IPs.json→src/config/IPs.json +0 −0 Viewed File moved. View file src/config/config.py 0 → 100644 +45 −0 Viewed Changes for src/config/config.py: 45 added lines, 0 removed lines. Original line number Diff line number Diff line import os from dotenv import load_dotenv from flask import Flask import logging # Load .env file if present load_dotenv() LOG_LEVELS = { "CRITICAL": logging.CRITICAL, "ERROR": logging.ERROR, "WARNING": logging.WARNING, "INFO": logging.INFO, "DEBUG": logging.DEBUG, "NOTSET": logging.NOTSET, } def create_config(app: Flask): """Load flags into Flask app.config""" # Default logging level app.config["LOGGING_LEVEL"] = LOG_LEVELS.get(os.getenv("LOGGING_LEVEL", "INFO").upper(),logging.INFO) # Dump templates app.config["DUMP_TEMPLATES"] = os.getenv("DUMP_TEMPLATES", "false").lower() == "true" # Mapper app.config["NRP_ENABLED"] = os.getenv("NRP_ENABLED", "false").lower() == "true" app.config["PLANNER_ENABLED"] = os.getenv("PLANNER_ENABLED", "false").lower() == "true" app.config["PCE_EXTERNAL"] = os.getenv("PCE_EXTERNAL", "false").lower() == "true" # Realizer app.config["DUMMY_MODE"] = os.getenv("DUMMY_MODE", "true").lower() == "true" # Teraflow app.config["TFS_IP"] = os.getenv("TFS_IP", "127.0.0.1") app.config["UPLOAD_TYPE"] = os.getenv("UPLOAD_TYPE", "WEBUI") app.config["TFS_L2VPN_SUPPORT"] = os.getenv("TFS_L2VPN_SUPPORT", "false").lower() == "true" # IXIA app.config["IXIA_IP"] = os.getenv("IXIA_IP", "127.0.0.1") # WebUI app.config["WEBUI_DEPLOY"] = os.getenv("WEBUI_DEPLOY", "false").lower() == "true" return app src/Constants.py→src/config/constants.py +6 −43 Viewed Changes for src/config/constants.py: 6 added lines, 43 removed lines. Original line number Diff line number Diff line Loading @@ -13,56 +13,19 @@ # limitations under the License. # This file includes original contributions from Telefonica Innovación Digital S.L. import logging, os, json # Default logging level DEFAULT_LOGGING_LEVEL = logging.INFO from pathlib import Path # Default port for NSC deployment NSC_PORT = 8081 # Paths # Obtain the absolute path of the current file SRC_PATH = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(SRC_PATH, 'IPs.json')) as f: ips = json.load(f) # Create the path to the desired file relative to the current file TEMPLATES_PATH = os.path.join(SRC_PATH, "templates") # Dump templates DUMP_TEMPLATES = False # Mapper # Flag to determine if the NSC performs NRPs NRP_ENABLED = False # Planner Flags PLANNER_ENABLED = True # Flag to determine if external PCE is used PCE_EXTERNAL = False # Realizer # Controller Flags # If True, config is not sent to controllers DUMMY_MODE = False #####TERAFLOW##### # Teraflow IP TFS_IP = ips.get('TFS_IP') UPLOAD_TYPE = "WEBUI" # "WEBUI" or "NBI" BASE_DIR = Path(__file__).resolve().parent.parent.parent SRC_PATH = BASE_DIR / "src" TEMPLATES_PATH = SRC_PATH / "templates" DATABASE_PATH = SRC_PATH / "database" CONFIG_PATH = SRC_PATH / "config" NBI_L2_PATH = "restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services" NBI_L3_PATH = "restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services" # Flag to determine if additional L2VPN configuration support is required for deploying L2VPNs with path selection TFS_L2VPN_SUPPORT = False #####IXIA##### # IXIA NEII IP IXIA_IP = ips.get('IXIA_IP') # WebUI # Flag to deploy the WebUI WEBUI_DEPLOY = True No newline at end of file Loading
src/api/main.py 0 → 100644 +193 −0 Viewed Changes for src/api/main.py: 193 added lines, 0 removed lines. Original line number Diff line number Diff line from src.config.constants import DATABASE_PATH from src.utils.send_response import send_response import os, json, logging from flask import current_app class Api: def __init__(self, slice_service): self.slice_service = slice_service def add_flow(self, intent): """ Create a new transport network slice. Args: intent (dict): Network slice intent in 3GPP or IETF format Returns: Result of the Network Slice Controller (NSC) operation API Endpoint: POST /slice Raises: ValueError: If no transport network slices are found Exception: For unexpected errors during slice creation process """ try: result = self.slice_service.nsc(intent) if not result: return send_response(False, code=404, message="No intents found") return send_response( True, code=201, data=result ) except Exception as e: # Handle unexpected errors return send_response(False, code=500, message=str(e)) def get_flows(self,slice_id=None): """ Retrieve transport network slice information. This method allows retrieving: - All transport network slices - A specific slice by its ID Args: slice_id (str, optional): Unique identifier of a specific slice. Defaults to None. Returns: dict or list: - If slice_id is provided: Returns the specific slice details - If slice_id is None: Returns a list of all slices - Returns an error response if no slices are found API Endpoint: GET /slice/{id} Raises: ValueError: If no transport network slices are found Exception: For unexpected errors during file processing """ try: # Read slice database from JSON file with open(os.path.join(DATABASE_PATH, "slice_ddbb.json"), 'r') as file: content = json.load(file) # If specific slice ID is provided, find and return matching slice if slice_id: for slice in content: if slice["slice_id"] == slice_id: return slice, 200 raise ValueError("Transport network slices not found") # If no slices exist, raise an error if len(content) == 0: raise ValueError("Transport network slices not found") # Return all slices if no specific ID is given return [slice for slice in content if slice.get("controller") == self.slice_service.controller_type], 200 except ValueError as e: # Handle case where no slices are found return send_response(False, code=404, message=str(e)) except Exception as e: # Handle unexpected errors return send_response(False, code=500, message=str(e)) def modify_flow(self,slice_id, intent): """ Modify an existing transport network slice. Args: slice_id (str): Unique identifier of the slice to modify intent (dict): New intent configuration for the slice Returns: Result of the Network Slice Controller (NSC) operation API Endpoint: PUT /slice/{id} """ try: result = self.slice_service.nsc(intent, slice_id) if not result: return send_response(False, code=404, message="Slice not found") return send_response( True, code=200, message="Slice modified successfully", data=result ) except Exception as e: # Handle unexpected errors return send_response(False, code=500, message=str(e)) def delete_flows(self, slice_id=None): """ Delete transport network slice(s). This method supports: - Deleting a specific slice by ID - Deleting all slices - Optional cleanup of L2VPN configurations Args: slice_id (str, optional): Unique identifier of slice to delete. Defaults to None. Returns: dict: Response indicating successful deletion or error details API Endpoint: DELETE /slice/{id} Raises: ValueError: If no slices are found to delete Exception: For unexpected errors during deletion process Notes: - If controller_type is TFS, attempts to delete from Teraflow - If need_l2vpn_support is True, performs additional L2VPN cleanup """ try: # Read current slice database with open(os.path.join(DATABASE_PATH, "slice_ddbb.json"), 'r') as file: content = json.load(file) id = None # Delete specific slice if slice_id is provided if slice_id: for i, slice in enumerate(content): if slice["slice_id"] == slice_id and slice.get("controller") == self.slice_service.controller_type: del content[i] id = i break # Raise error if slice not found if id is None: raise ValueError("Transport network slice not found") # Update slice database with open(os.path.join(DATABASE_PATH, "slice_ddbb.json"), 'w') as file: json.dump(content, file, indent=4) logging.info(f"Slice {slice_id} removed successfully") return {}, 204 # Delete all slices else: # Optional: Delete in Teraflow if configured if self.slice_service.controller_type == "TFS": # TODO: should send a delete request to Teraflow if current_app.config["TFS_L2VPN_SUPPORT"]: self.slice_service.tfs_l2vpn_delete() data_removed = [slice for slice in content if slice.get("controller") == self.slice_service.controller_type] # Verify slices exist before deletion if len(data_removed) == 0: raise ValueError("Transport network slices not found") filtered_data = [slice for slice in content if slice.get("controller") != self.slice_service.controller_type] # Clear slice database with open(os.path.join(DATABASE_PATH, "slice_ddbb.json"), 'w') as file: json.dump(filtered_data, file, indent=4) logging.info("All slices removed successfully") return {}, 204 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
src/config/.env.example 0 → 100644 +39 −0 Viewed Changes for src/config/.env.example: 39 added lines, 0 removed lines. Original line number Diff line number Diff line # ------------------------- # General # ------------------------- LOGGING_LEVEL=INFO # Options: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET DUMP_TEMPLATES=false # ------------------------- # Mapper # ------------------------- # Flag to determine if the NSC performs NRPs NRP_ENABLED=false # Planner Flags PLANNER_ENABLED=true # Flag to determine if external PCE is used PCE_EXTERNAL=false # ------------------------- # Realizer # ------------------------- # If true, no config sent to controllers DUMMY_MODE=true # ------------------------- # Teraflow # ------------------------- TFS_IP=127.0.0.1 UPLOAD_TYPE=WEBUI # Options: WEBUI o NBI # Flag to determine if additional L2VPN configuration support is required for deploying L2VPNs with path selection TFS_L2VPN_SUPPORT=false # ------------------------- # IXIA # ------------------------- IXIA_IP=127.0.0.1 # ------------------------- # WebUI # ------------------------- WEBUI_DEPLOY=true
src/config/config.py 0 → 100644 +45 −0 Viewed Changes for src/config/config.py: 45 added lines, 0 removed lines. Original line number Diff line number Diff line import os from dotenv import load_dotenv from flask import Flask import logging # Load .env file if present load_dotenv() LOG_LEVELS = { "CRITICAL": logging.CRITICAL, "ERROR": logging.ERROR, "WARNING": logging.WARNING, "INFO": logging.INFO, "DEBUG": logging.DEBUG, "NOTSET": logging.NOTSET, } def create_config(app: Flask): """Load flags into Flask app.config""" # Default logging level app.config["LOGGING_LEVEL"] = LOG_LEVELS.get(os.getenv("LOGGING_LEVEL", "INFO").upper(),logging.INFO) # Dump templates app.config["DUMP_TEMPLATES"] = os.getenv("DUMP_TEMPLATES", "false").lower() == "true" # Mapper app.config["NRP_ENABLED"] = os.getenv("NRP_ENABLED", "false").lower() == "true" app.config["PLANNER_ENABLED"] = os.getenv("PLANNER_ENABLED", "false").lower() == "true" app.config["PCE_EXTERNAL"] = os.getenv("PCE_EXTERNAL", "false").lower() == "true" # Realizer app.config["DUMMY_MODE"] = os.getenv("DUMMY_MODE", "true").lower() == "true" # Teraflow app.config["TFS_IP"] = os.getenv("TFS_IP", "127.0.0.1") app.config["UPLOAD_TYPE"] = os.getenv("UPLOAD_TYPE", "WEBUI") app.config["TFS_L2VPN_SUPPORT"] = os.getenv("TFS_L2VPN_SUPPORT", "false").lower() == "true" # IXIA app.config["IXIA_IP"] = os.getenv("IXIA_IP", "127.0.0.1") # WebUI app.config["WEBUI_DEPLOY"] = os.getenv("WEBUI_DEPLOY", "false").lower() == "true" return app
src/Constants.py→src/config/constants.py +6 −43 Viewed Changes for src/config/constants.py: 6 added lines, 43 removed lines. Original line number Diff line number Diff line Loading @@ -13,56 +13,19 @@ # limitations under the License. # This file includes original contributions from Telefonica Innovación Digital S.L. import logging, os, json # Default logging level DEFAULT_LOGGING_LEVEL = logging.INFO from pathlib import Path # Default port for NSC deployment NSC_PORT = 8081 # Paths # Obtain the absolute path of the current file SRC_PATH = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(SRC_PATH, 'IPs.json')) as f: ips = json.load(f) # Create the path to the desired file relative to the current file TEMPLATES_PATH = os.path.join(SRC_PATH, "templates") # Dump templates DUMP_TEMPLATES = False # Mapper # Flag to determine if the NSC performs NRPs NRP_ENABLED = False # Planner Flags PLANNER_ENABLED = True # Flag to determine if external PCE is used PCE_EXTERNAL = False # Realizer # Controller Flags # If True, config is not sent to controllers DUMMY_MODE = False #####TERAFLOW##### # Teraflow IP TFS_IP = ips.get('TFS_IP') UPLOAD_TYPE = "WEBUI" # "WEBUI" or "NBI" BASE_DIR = Path(__file__).resolve().parent.parent.parent SRC_PATH = BASE_DIR / "src" TEMPLATES_PATH = SRC_PATH / "templates" DATABASE_PATH = SRC_PATH / "database" CONFIG_PATH = SRC_PATH / "config" NBI_L2_PATH = "restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services" NBI_L3_PATH = "restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services" # Flag to determine if additional L2VPN configuration support is required for deploying L2VPNs with path selection TFS_L2VPN_SUPPORT = False #####IXIA##### # IXIA NEII IP IXIA_IP = ips.get('IXIA_IP') # WebUI # Flag to deploy the WebUI WEBUI_DEPLOY = True No newline at end of file