diff --git a/src/api/main.py b/src/api/main.py index 8261a9bcb49ef8a84a81f38cbf656570c7ae4208..344171c982dc5400741a8c26cf9c8c8300ee5f8a 100644 --- a/src/api/main.py +++ b/src/api/main.py @@ -18,6 +18,8 @@ from src.utils.send_response import send_response import logging from flask import current_app from src.database.db import get_data, delete_data, get_all_data, delete_all_data +from src.realizer.tfs.helpers.tfs_connector import tfs_connector +from src.utils.safe_get import safe_get class Api: def __init__(self, slice_service): @@ -44,7 +46,7 @@ class Api: result = self.slice_service.nsc(intent) if not result: return send_response(False, code=404, message="No intents found") - + logging.info(f"Slice created successfully") return send_response( True, code=201, @@ -120,7 +122,7 @@ class Api: result = self.slice_service.nsc(intent, slice_id) if not result: return send_response(False, code=404, message="Slice not found") - + logging.info(f"Slice {slice_id} modified successfully") return send_response( True, code=200, @@ -165,6 +167,14 @@ class Api: # Raise error if slice not found if not slice or slice.get("controller") != self.slice_service.controller_type: raise ValueError("Transport network slice not found") + # Delete in Teraflow + if not current_app.config["DUMMY_MODE"]: + if self.slice_service.controller_type == "TFS": + slice_type = safe_get(slice, ['intent', 'ietf-network-slice-service:network-slice-services', 'slice-service', 0, 'service-tags', 'tag-type', 0, 'tag-type-value', 0]) + if not slice_type: + slice_type = "L2" + logging.warning(f"Slice type not found in slice intent. Defaulting to L2") + tfs_connector().nbi_delete(current_app.config["TFS_IP"],slice_type, slice_id) # Update slice database delete_data(slice_id) logging.info(f"Slice {slice_id} removed successfully") @@ -173,10 +183,18 @@ class Api: # 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() + if not current_app.config["DUMMY_MODE"]: + if self.slice_service.controller_type == "TFS": + content = get_all_data() + for slice in content: + if slice.get("controller") == self.slice_service.controller_type: + slice_type = safe_get(slice, ['intent', 'ietf-network-slice-service:network-slice-services', 'slice-service', 0, 'service-tags', 'tag-type', 0, 'tag-type-value', 0]) + if not slice_type: + slice_type = "L2" + logging.warning(f"Slice type not found in slice intent. Defaulting to L2") + tfs_connector().nbi_delete(current_app.config["TFS_IP"],slice_type, slice.get("slice_id")) + if current_app.config["TFS_L2VPN_SUPPORT"]: + self.slice_service.tfs_l2vpn_delete() # Clear slice database delete_all_data() diff --git a/src/realizer/tfs/helpers/tfs_connector.py b/src/realizer/tfs/helpers/tfs_connector.py index 072acff54fcd20bbdcf73a875e0c65727d45db4f..dfcfeef45a07cc5863265461b6e00ca0cc3c368d 100644 --- a/src/realizer/tfs/helpers/tfs_connector.py +++ b/src/realizer/tfs/helpers/tfs_connector.py @@ -15,6 +15,7 @@ # This file includes original contributions from Telefonica Innovación Digital S.L. import logging, requests, json +from src.config.constants import NBI_L2_PATH, NBI_L3_PATH class tfs_connector(): def webui_post(self, tfs_ip, service): @@ -50,5 +51,22 @@ class tfs_connector(): logging.debug("Posting to TFS NBI: %s",data) token={'csrf_token':token} response = session.post(url,headers=headers,data=data,timeout=60) + response.raise_for_status() + logging.debug("Http response: %s",response.text) + return response + + def nbi_delete(self, tfs_ip: str, service_type: str , service_id: str) -> requests.Response: + user="admin" + password="admin" + url = f'http://{user}:{password}@{tfs_ip}' + if service_type == 'L2': + url = url + f'/{NBI_L2_PATH}/vpn-service={service_id}' + elif service_type == 'L3': + url = url + f'/{NBI_L3_PATH}/vpn-service={service_id}' + else: + raise ValueError("Invalid service type. Use 'L2' or 'L3'.") + response = requests.delete(url, timeout=60) + response.raise_for_status() + logging.debug('Service deleted successfully') logging.debug("Http response: %s",response.text) return response \ No newline at end of file diff --git a/src/realizer/tfs/service_types/tfs_l2vpn.py b/src/realizer/tfs/service_types/tfs_l2vpn.py index 2845dfec5d8e230d1c9e9ebe2d0b1216cc9ee767..ca190fbc84bee65dba713d0690f8e10e26c035a6 100644 --- a/src/realizer/tfs/service_types/tfs_l2vpn.py +++ b/src/realizer/tfs/service_types/tfs_l2vpn.py @@ -15,7 +15,6 @@ # This file includes original contributions from Telefonica Innovación Digital S.L. import logging, os -from datetime import datetime from src.config.constants import TEMPLATES_PATH, NBI_L2_PATH from src.utils.load_template import load_template from ..helpers.cisco_connector import cisco_connector @@ -53,8 +52,8 @@ def tfs_l2vpn(ietf_intent, response): # Load L2VPN service template tfs_request = load_template(os.path.join(TEMPLATES_PATH, "L2-VPN_template_empty.json"))["services"][0] - # Generate unique service UUID - tfs_request["service_id"]["service_uuid"]["uuid"] += "-" + str(int(datetime.now().timestamp() * 1e7)) + # Configure service UUID + tfs_request["service_id"]["service_uuid"]["uuid"] = ietf_intent['ietf-network-slice-service:network-slice-services']['slice-service'][0]["id"] # Configure service endpoints for endpoint in tfs_request["service_endpoint_ids"]: @@ -62,7 +61,7 @@ def tfs_l2vpn(ietf_intent, response): endpoint["endpoint_uuid"]["uuid"] = origin_router_if if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_if # Add service constraints - for constraint in slice.get("QoS Requirements", []): + for constraint in slice.get("requirements", []): tfs_request["service_constraints"].append({"custom": constraint}) # Add configuration rules diff --git a/src/realizer/tfs/service_types/tfs_l3vpn.py b/src/realizer/tfs/service_types/tfs_l3vpn.py index 22ae62a6d4e4bd0f9fbc0502744a4e1be88082d8..ff8f0816a6697a9c48fd9c8c2fc9e3739c13cdd1 100644 --- a/src/realizer/tfs/service_types/tfs_l3vpn.py +++ b/src/realizer/tfs/service_types/tfs_l3vpn.py @@ -15,7 +15,6 @@ # This file includes original contributions from Telefonica Innovación Digital S.L. import logging, os -from datetime import datetime from src.config.constants import TEMPLATES_PATH, NBI_L3_PATH from src.utils.load_template import load_template from flask import current_app @@ -50,8 +49,8 @@ def tfs_l3vpn(ietf_intent, response): if current_app.config["UPLOAD_TYPE"] == "WEBUI": # Load L3VPN service template tfs_request = load_template(os.path.join(TEMPLATES_PATH, "L3-VPN_template_empty.json"))["services"][0] - # Generate unique service UUID - tfs_request["service_id"]["service_uuid"]["uuid"] += "-" + str(int(datetime.now().timestamp() * 1e7)) + # Configure service UUID + tfs_request["service_id"]["service_uuid"]["uuid"] = ietf_intent['ietf-network-slice-service:network-slice-services']['slice-service'][0]["id"] # Configure service endpoints for endpoint in tfs_request["service_endpoint_ids"]: @@ -59,7 +58,7 @@ def tfs_l3vpn(ietf_intent, response): endpoint["endpoint_uuid"]["uuid"] = origin_router_if if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_if # Add service constraints - for constraint in slice.get("QoS Requirements", []): + for constraint in slice.get("requirements", []): tfs_request["service_constraints"].append({"custom": constraint}) # Add configuration rules @@ -112,7 +111,7 @@ def tfs_l3vpn(ietf_intent, response): access["vpn-attachment"]["vpn-id"] = full_id # Aplicar restricciones QoS - for constraint in slice.get("QoS Requirements", []): + for constraint in slice.get("requirements", []): ctype = constraint["constraint_type"] cvalue = float(constraint["constraint_value"]) if constraint["constraint_type"].startswith("one-way-bandwidth"):