Loading src/Constants.py +6 −3 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2025 Telefonica Innovación Digital S.L. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # This file is an original contribution from Telefonica Innovación Digital S.L. import logging, os # Default logging level DEFAULT_LOGGING_LEVEL = logging.INFO Loading @@ -33,5 +31,10 @@ TEMPLATES_PATH = os.path.join(SRC_PATH, "templates") TFS_UPLOAD = False # Teraflow IP TFS_IP = "192.168.165.10" #path = "restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services" path_l3 = "restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services" #json_file = "/home/tfs/nsc-hackfest7/src/hackfest7/slice_request.json" #/home/tfs/nsc-hackfest7/src/hackfest7/slice_request.json # Flag to determine if additional L2VPN configuration support is required for deploying L2VPNs with path selection TFS_L2VPN_SUPPORT = False No newline at end of file src/helpers.py +31 −16 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2025 Telefonica Innovación Digital S.L. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -15,6 +15,7 @@ import logging, requests, json from netmiko import ConnectHandler from src.Constants import DEFAULT_LOGGING_LEVEL from requests.auth import HTTPBasicAuth # Configure logging to provide clear and informative log messages logging.basicConfig( Loading @@ -23,25 +24,39 @@ logging.basicConfig( #Teraflow class tfs_connector(): #curl -X POST -H "Content-Type: application/json" --user "admin:admin" --data @ietf-l3vpn-service.json http://10.1.7.197/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services def simple_post(self, tfs_ip, service): def simple_post(self, tfs_ip,service,path): user="admin" password="admin" token="" session = requests.Session() session.auth = (user, password) url=f'http://{tfs_ip}/webui' response=session.get(url=url) for item in response.iter_lines(): if("csrf_token" in str(item)): string=str(item).split('<input id="csrf_token" name="csrf_token" type="hidden" value=')[1] token=string.split(">")[0].strip('"') logging.debug("csrf token %s",token) files = {'descriptors': ("data.json", json.dumps(service).encode("utf-8"), "application/json")} #session = requests.Session() #session.auth = (user, password) #url = f'http://{tfs_ip}/{path}' #url_l2 = f'http://{user}:{password}@{tfs_ip}/{path}' url_l3 = f'http://{user}:{password}@{tfs_ip}/{path_l3}' logging.info(service) #response=session.get(url=url) #for item in response.iter_lines(): # if("csrf_token" in str(item)): # string=str(item).split('<input id="csrf_token" name="csrf_token" type="hidden" value=')[1] # token=string.split(">")[0].strip('"') #logging.debug("csrf token %s",token) #files = {'descriptors': ("data.json", json.dumps(service).encode("utf-8"), "application/json")} headers = {'Content-Type': 'application/json'} json_data = json.dumps(service) #json_url="/home/tfs/nsc-hackfest7/our-temp/ietf-l3vpn-service.json" #with open(json_url, 'r', encoding="UTF-8") as file: # json_data = json.load(file) # logging.debug("this is the JSON data that we have") # logging.debug(json_data) # json_decoded = json.dumps(json_data) json_decoded = json_data token={'csrf_token':token} response = session.post(url,files=files,data=token,timeout=60) logging.debug("Http response: %s",response.text) response = requests.post(url_l2, headers=headers, data=json_decoded, timeout=60) logging.info(response.text) #logging.debug("Http response: %s",response.text) return response #CISCO Loading src/network_slice_controller.py +135 −74 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2025 Telefonica Innovación Digital S.L. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -15,7 +15,7 @@ import json, time, os, logging, uuid from datetime import datetime from src.helpers import tfs_connector, cisco_connector from src.Constants import DEFAULT_LOGGING_LEVEL, TFS_UPLOAD, TFS_IP, TFS_L2VPN_SUPPORT, SRC_PATH, TEMPLATES_PATH from src.Constants import DEFAULT_LOGGING_LEVEL, TFS_UPLOAD, TFS_IP, TFS_L2VPN_SUPPORT, SRC_PATH, TEMPLATES_PATH, path, path_l3 # Configure logging to provide clear and informative log messages logging.basicConfig( Loading @@ -38,7 +38,7 @@ class NSController: - Slice Realization: Convert intents to specific network configurations (L2VPN, L3VPN) """ def __init__(self, upload_to_tfs = TFS_UPLOAD, tfs_ip=TFS_IP, need_l2vpn_support=TFS_L2VPN_SUPPORT): def __init__(self, upload_to_tfs = TFS_UPLOAD, tfs_ip=TFS_IP,path=path, need_l2vpn_support=TFS_L2VPN_SUPPORT): """ Initialize the Network Slice Controller. Loading @@ -58,6 +58,9 @@ class NSController: """ self.upload_to_tfs = upload_to_tfs self.tfs_ip = tfs_ip self.path = path self.path_l3 = path_l3 #self.json_file = json_file self.answer = {} self.cool_answer = {} self.start_time = 0 Loading Loading @@ -257,7 +260,8 @@ class NSController: # Reset requests and load IETF template self.__load_template(1, os.path.join(TEMPLATES_PATH, "ietf_template_empty.json")) tfs_requests = {"services":[]} #tfs_requests = {"services":[]} tfs_requests = {} # Process intent (translate if 3GPP) ietf_intents = self.__nbi_processor(intent_json) Loading @@ -271,23 +275,24 @@ class NSController: self.__mapper(intent) # Realizer tfs_request = self.__realizer(intent) tfs_requests["services"].append(tfs_request) #tfs_requests.append(tfs_request) tfs_requests = tfs_request else: return self.__send_response(False, code=404, message="No intents found") # Generated service logging.debug(json.dumps(tfs_requests, indent=2)) #logging.debug(json.dumps(tfs_requests, indent=2)) # Optional: Upload template to Teraflow if self.upload_to_tfs == True: response = tfs_connector().simple_post(self.tfs_ip, tfs_requests) response = tfs_connector().simple_post(self.tfs_ip, tfs_requests, self.path_l3) if not response.ok: return self.__send_response(False, code=response.status_code, message=f"Teraflow upload failed. Response: {response.text}") # if not response.ok: # return self.__send_response(False, code=response.status_code, message=f"Teraflow upload failed. Response: {response.text}") # For deploying an L2VPN with path selection (not supported by Teraflow) if self.need_l2vpn_support: self.__tfs_l2vpn_support(tfs_requests["services"]) # if self.need_l2vpn_support: # self.__tfs_l2vpn_support(tfs_requests["services"]) logging.info("Request sent to Teraflow") Loading Loading @@ -405,7 +410,7 @@ class NSController: self.__nrp(order, nrp) else: # Select slice service method return self.__select_way("L2VPN", ietf_intent) return self.__select_way("L3VPN", ietf_intent) ### Generic functionalities def __load_template(self, which, dir_t): Loading Loading @@ -461,6 +466,7 @@ class NSController: "setup_time": self.setup_time } # Add slice details to the response logging.info(self.answer) for subnet in self.answer: slice_info = { "id": subnet, Loading Loading @@ -847,7 +853,6 @@ class NSController: elif way == "L3VPN": realizing_request = self.__tfs_l3vpn(ietf_intent) return realizing_request def __tfs_l2vpn(self, ietf_intent): """ Translate slice intent into a TeraFlow service request. Loading @@ -870,53 +875,109 @@ class NSController: # Hardcoded router endpoints # TODO (should be dynamically determined) origin_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] origin_router_if = '0/0/0-GigabitEthernet0/0/0/0' origin_router_if = 'eth2' destination_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] destination_router_if = '0/0/3-GigabitEthernet0/0/0/3' destination_router_if = 'eth2' # Extract QoS Profile from intent QoSProfile = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["id"] vlan_value = 0 # Load L2VPN service template self.__load_template(2, os.path.join(TEMPLATES_PATH, "L2-VPN_template_empty.json")) tfs_request = json.loads(str(self.__teraflow_template))["services"][0] # Generate unique service UUID tfs_request["service_id"]["service_uuid"]["uuid"] += "-" + str(int(datetime.now().timestamp() * 1e7)) # Configure service endpoints for endpoint in tfs_request["service_endpoint_ids"]: endpoint["device_id"]["device_uuid"]["uuid"] = origin_router_id if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_id endpoint["endpoint_uuid"]["uuid"] = origin_router_if if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_if self.__load_template(2, os.path.join(TEMPLATES_PATH, "ietfL2VPN_template.json")) tfs_request = json.loads(str(self.__teraflow_template)) full_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["id"] uuid_only = full_id.split("slice-service-")[-1] tfs_request["ietf-l2vpn-svc:vpn-service"][0]["vpn-id"] = uuid_only for site in tfs_request["ietf-l2vpn-svc:vpn-service"][0]["site"]: if site is tfs_request["ietf-l2vpn-svc:vpn-service"][0]["site"][0]: site["site-id"] = origin_router_id site["site-location"] = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["node-id"] site["site-network-access"]["interface"]["ip-address"] = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["sdp-ip-address"] else: site["site-id"] = destination_router_id site["site-location"] = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["node-id"] site["site-network-access"]["interface"]["ip-address"] = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["sdp-ip-address"] self.answer[self.subnet]["QoS Requirements"] = [] # Add service constraints for i, constraint in enumerate(tfs_request["service_constraints"]): bound = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["bound"] self.answer[self.subnet]["QoS Requirements"].append(bound) constraint["custom"]["constraint_value"] = str(bound) # Add configuration rules for i, config_rule in enumerate(tfs_request["service_config"]["config_rules"][1:], start=1): router_id = origin_router_id if i == 1 else destination_router_id router_if = origin_router_if if i == 1 else destination_router_if resource_value = config_rule["custom"]["resource_value"] sdp_index = i - 1 vlan_value = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][sdp_index]["service-match-criteria"]["match-criterion"][0]["value"] resource_value["vlan_id"] = int(vlan_value) resource_value["circuit_id"] = vlan_value resource_value["remote_router"] = destination_router_id if i == 1 else origin_router_id resource_value["ni_name"] = 'ELAN{:s}'.format(str(vlan_value)) config_rule["custom"]["resource_key"] = f"/device[{router_id}]/endpoint[{router_if}]/settings" for i in range(len(ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"])): logging.info(self.answer[self.subnet]) self.answer[self.subnet]["QoS Requirements"].append(ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["bound"]) # Log and store VLAN information logging.info(f"Intent with VLAN {vlan_value} realized\n") self.answer[self.subnet]["VLAN"] = vlan_value logging.info(tfs_request) with open(os.path.join(TEMPLATES_PATH, "l2vpn_request.json"), "w") as archivo: archivo.write(json.dumps(tfs_request,indent=2)) return tfs_request # def __tfs_l2vpn(self, ietf_intent): # """ # Translate slice intent into a TeraFlow service request. # # This method prepares a L2VPN service request by: # 1. Defining endpoint routers # 2. Loading a service template # 3. Generating a unique service UUID # 4. Configuring service endpoints # 5. Adding QoS constraints # 6. Preparing configuration rules for network interfaces # # Args: # ietf_intent (dict): IETF-formatted network slice intent. # # Returns: # dict: A TeraFlow service request for L2VPN configuration. # # """ # # Hardcoded router endpoints # # TODO (should be dynamically determined) # origin_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] # origin_router_if = 'eth2' # destination_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] # destination_router_if = 'eth2' # # # Extract QoS Profile from intent # QoSProfile = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["id"] # # vlan_value = 0 # # # Load L2VPN service template # self.__load_template(2, os.path.join(TEMPLATES_PATH, "ietfL2VPN_template")) # tfs_request = json.loads(str(self.__teraflow_template))["services"][0] # # # Generate unique service UUID # tfs_request["service_id"]["service_uuid"]["uuid"] += "-" + str(int(datetime.now().timestamp() * 1e7)) # # # Configure service endpoints # for endpoint in tfs_request["service_endpoint_ids"]: # endpoint["device_id"]["device_uuid"]["uuid"] = origin_router_id if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_id # endpoint["endpoint_uuid"]["uuid"] = origin_router_if if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_if # # self.answer[self.subnet]["QoS Requirements"] = [] # # Add service constraints # for i, constraint in enumerate(tfs_request["service_constraints"]): # bound = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["bound"] # self.answer[self.subnet]["QoS Requirements"].append(bound) # constraint["custom"]["constraint_value"] = str(bound) # # # Add configuration rules # for i, config_rule in enumerate(tfs_request["service_config"]["config_rules"][1:], start=1): # router_id = origin_router_id if i == 1 else destination_router_id # router_if = origin_router_if if i == 1 else destination_router_if # resource_value = config_rule["custom"]["resource_value"] # # sdp_index = i - 1 # vlan_value = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][sdp_index]["service-match-criteria"]["match-criterion"][0]["value"] # resource_value["vlan_id"] = int(vlan_value) # resource_value["circuit_id"] = vlan_value # resource_value["remote_router"] = destination_router_id if i == 1 else origin_router_id # resource_value["ni_name"] = 'ELAN{:s}'.format(str(vlan_value)) # config_rule["custom"]["resource_key"] = f"/device[{router_id}]/endpoint[{router_if}]/settings" # # # Log and store VLAN information # logging.info(f"Intent with VLAN {vlan_value} realized\n") # self.answer[self.subnet]["VLAN"] = vlan_value # return tfs_request # def __tfs_l2vpn_support(self, requests): """ Configuration support for L2VPN with path selection based on MPLS traffic-engineering tunnels Loading Loading @@ -1006,11 +1067,11 @@ class NSController: dict: A TeraFlow service request for L3VPN configuration. """ # Hardcoded router endpoints # TODO should be dynamically determined # TODO (should be dynamically determined) origin_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] origin_router_if = '0/0/0-GigabitEthernet0/0/0/0' destination_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] destination_router_if = '0/0/3-GigabitEthernet0/0/0/3' destination_router_if = 'eth2' # Load L3VPN service template self.__load_template(2, os.path.join(TEMPLATES_PATH, "L3-VPN_template_empty.json")) Loading Loading
src/Constants.py +6 −3 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2025 Telefonica Innovación Digital S.L. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # This file is an original contribution from Telefonica Innovación Digital S.L. import logging, os # Default logging level DEFAULT_LOGGING_LEVEL = logging.INFO Loading @@ -33,5 +31,10 @@ TEMPLATES_PATH = os.path.join(SRC_PATH, "templates") TFS_UPLOAD = False # Teraflow IP TFS_IP = "192.168.165.10" #path = "restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services" path_l3 = "restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services" #json_file = "/home/tfs/nsc-hackfest7/src/hackfest7/slice_request.json" #/home/tfs/nsc-hackfest7/src/hackfest7/slice_request.json # Flag to determine if additional L2VPN configuration support is required for deploying L2VPNs with path selection TFS_L2VPN_SUPPORT = False No newline at end of file
src/helpers.py +31 −16 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2025 Telefonica Innovación Digital S.L. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -15,6 +15,7 @@ import logging, requests, json from netmiko import ConnectHandler from src.Constants import DEFAULT_LOGGING_LEVEL from requests.auth import HTTPBasicAuth # Configure logging to provide clear and informative log messages logging.basicConfig( Loading @@ -23,25 +24,39 @@ logging.basicConfig( #Teraflow class tfs_connector(): #curl -X POST -H "Content-Type: application/json" --user "admin:admin" --data @ietf-l3vpn-service.json http://10.1.7.197/restconf/data/ietf-l3vpn-svc:l3vpn-svc/vpn-services def simple_post(self, tfs_ip, service): def simple_post(self, tfs_ip,service,path): user="admin" password="admin" token="" session = requests.Session() session.auth = (user, password) url=f'http://{tfs_ip}/webui' response=session.get(url=url) for item in response.iter_lines(): if("csrf_token" in str(item)): string=str(item).split('<input id="csrf_token" name="csrf_token" type="hidden" value=')[1] token=string.split(">")[0].strip('"') logging.debug("csrf token %s",token) files = {'descriptors': ("data.json", json.dumps(service).encode("utf-8"), "application/json")} #session = requests.Session() #session.auth = (user, password) #url = f'http://{tfs_ip}/{path}' #url_l2 = f'http://{user}:{password}@{tfs_ip}/{path}' url_l3 = f'http://{user}:{password}@{tfs_ip}/{path_l3}' logging.info(service) #response=session.get(url=url) #for item in response.iter_lines(): # if("csrf_token" in str(item)): # string=str(item).split('<input id="csrf_token" name="csrf_token" type="hidden" value=')[1] # token=string.split(">")[0].strip('"') #logging.debug("csrf token %s",token) #files = {'descriptors': ("data.json", json.dumps(service).encode("utf-8"), "application/json")} headers = {'Content-Type': 'application/json'} json_data = json.dumps(service) #json_url="/home/tfs/nsc-hackfest7/our-temp/ietf-l3vpn-service.json" #with open(json_url, 'r', encoding="UTF-8") as file: # json_data = json.load(file) # logging.debug("this is the JSON data that we have") # logging.debug(json_data) # json_decoded = json.dumps(json_data) json_decoded = json_data token={'csrf_token':token} response = session.post(url,files=files,data=token,timeout=60) logging.debug("Http response: %s",response.text) response = requests.post(url_l2, headers=headers, data=json_decoded, timeout=60) logging.info(response.text) #logging.debug("Http response: %s",response.text) return response #CISCO Loading
src/network_slice_controller.py +135 −74 Original line number Diff line number Diff line # Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2025 Telefonica Innovación Digital S.L. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. Loading @@ -15,7 +15,7 @@ import json, time, os, logging, uuid from datetime import datetime from src.helpers import tfs_connector, cisco_connector from src.Constants import DEFAULT_LOGGING_LEVEL, TFS_UPLOAD, TFS_IP, TFS_L2VPN_SUPPORT, SRC_PATH, TEMPLATES_PATH from src.Constants import DEFAULT_LOGGING_LEVEL, TFS_UPLOAD, TFS_IP, TFS_L2VPN_SUPPORT, SRC_PATH, TEMPLATES_PATH, path, path_l3 # Configure logging to provide clear and informative log messages logging.basicConfig( Loading @@ -38,7 +38,7 @@ class NSController: - Slice Realization: Convert intents to specific network configurations (L2VPN, L3VPN) """ def __init__(self, upload_to_tfs = TFS_UPLOAD, tfs_ip=TFS_IP, need_l2vpn_support=TFS_L2VPN_SUPPORT): def __init__(self, upload_to_tfs = TFS_UPLOAD, tfs_ip=TFS_IP,path=path, need_l2vpn_support=TFS_L2VPN_SUPPORT): """ Initialize the Network Slice Controller. Loading @@ -58,6 +58,9 @@ class NSController: """ self.upload_to_tfs = upload_to_tfs self.tfs_ip = tfs_ip self.path = path self.path_l3 = path_l3 #self.json_file = json_file self.answer = {} self.cool_answer = {} self.start_time = 0 Loading Loading @@ -257,7 +260,8 @@ class NSController: # Reset requests and load IETF template self.__load_template(1, os.path.join(TEMPLATES_PATH, "ietf_template_empty.json")) tfs_requests = {"services":[]} #tfs_requests = {"services":[]} tfs_requests = {} # Process intent (translate if 3GPP) ietf_intents = self.__nbi_processor(intent_json) Loading @@ -271,23 +275,24 @@ class NSController: self.__mapper(intent) # Realizer tfs_request = self.__realizer(intent) tfs_requests["services"].append(tfs_request) #tfs_requests.append(tfs_request) tfs_requests = tfs_request else: return self.__send_response(False, code=404, message="No intents found") # Generated service logging.debug(json.dumps(tfs_requests, indent=2)) #logging.debug(json.dumps(tfs_requests, indent=2)) # Optional: Upload template to Teraflow if self.upload_to_tfs == True: response = tfs_connector().simple_post(self.tfs_ip, tfs_requests) response = tfs_connector().simple_post(self.tfs_ip, tfs_requests, self.path_l3) if not response.ok: return self.__send_response(False, code=response.status_code, message=f"Teraflow upload failed. Response: {response.text}") # if not response.ok: # return self.__send_response(False, code=response.status_code, message=f"Teraflow upload failed. Response: {response.text}") # For deploying an L2VPN with path selection (not supported by Teraflow) if self.need_l2vpn_support: self.__tfs_l2vpn_support(tfs_requests["services"]) # if self.need_l2vpn_support: # self.__tfs_l2vpn_support(tfs_requests["services"]) logging.info("Request sent to Teraflow") Loading Loading @@ -405,7 +410,7 @@ class NSController: self.__nrp(order, nrp) else: # Select slice service method return self.__select_way("L2VPN", ietf_intent) return self.__select_way("L3VPN", ietf_intent) ### Generic functionalities def __load_template(self, which, dir_t): Loading Loading @@ -461,6 +466,7 @@ class NSController: "setup_time": self.setup_time } # Add slice details to the response logging.info(self.answer) for subnet in self.answer: slice_info = { "id": subnet, Loading Loading @@ -847,7 +853,6 @@ class NSController: elif way == "L3VPN": realizing_request = self.__tfs_l3vpn(ietf_intent) return realizing_request def __tfs_l2vpn(self, ietf_intent): """ Translate slice intent into a TeraFlow service request. Loading @@ -870,53 +875,109 @@ class NSController: # Hardcoded router endpoints # TODO (should be dynamically determined) origin_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] origin_router_if = '0/0/0-GigabitEthernet0/0/0/0' origin_router_if = 'eth2' destination_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] destination_router_if = '0/0/3-GigabitEthernet0/0/0/3' destination_router_if = 'eth2' # Extract QoS Profile from intent QoSProfile = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["id"] vlan_value = 0 # Load L2VPN service template self.__load_template(2, os.path.join(TEMPLATES_PATH, "L2-VPN_template_empty.json")) tfs_request = json.loads(str(self.__teraflow_template))["services"][0] # Generate unique service UUID tfs_request["service_id"]["service_uuid"]["uuid"] += "-" + str(int(datetime.now().timestamp() * 1e7)) # Configure service endpoints for endpoint in tfs_request["service_endpoint_ids"]: endpoint["device_id"]["device_uuid"]["uuid"] = origin_router_id if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_id endpoint["endpoint_uuid"]["uuid"] = origin_router_if if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_if self.__load_template(2, os.path.join(TEMPLATES_PATH, "ietfL2VPN_template.json")) tfs_request = json.loads(str(self.__teraflow_template)) full_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["id"] uuid_only = full_id.split("slice-service-")[-1] tfs_request["ietf-l2vpn-svc:vpn-service"][0]["vpn-id"] = uuid_only for site in tfs_request["ietf-l2vpn-svc:vpn-service"][0]["site"]: if site is tfs_request["ietf-l2vpn-svc:vpn-service"][0]["site"][0]: site["site-id"] = origin_router_id site["site-location"] = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["node-id"] site["site-network-access"]["interface"]["ip-address"] = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["sdp-ip-address"] else: site["site-id"] = destination_router_id site["site-location"] = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["node-id"] site["site-network-access"]["interface"]["ip-address"] = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["sdp-ip-address"] self.answer[self.subnet]["QoS Requirements"] = [] # Add service constraints for i, constraint in enumerate(tfs_request["service_constraints"]): bound = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["bound"] self.answer[self.subnet]["QoS Requirements"].append(bound) constraint["custom"]["constraint_value"] = str(bound) # Add configuration rules for i, config_rule in enumerate(tfs_request["service_config"]["config_rules"][1:], start=1): router_id = origin_router_id if i == 1 else destination_router_id router_if = origin_router_if if i == 1 else destination_router_if resource_value = config_rule["custom"]["resource_value"] sdp_index = i - 1 vlan_value = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][sdp_index]["service-match-criteria"]["match-criterion"][0]["value"] resource_value["vlan_id"] = int(vlan_value) resource_value["circuit_id"] = vlan_value resource_value["remote_router"] = destination_router_id if i == 1 else origin_router_id resource_value["ni_name"] = 'ELAN{:s}'.format(str(vlan_value)) config_rule["custom"]["resource_key"] = f"/device[{router_id}]/endpoint[{router_if}]/settings" for i in range(len(ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"])): logging.info(self.answer[self.subnet]) self.answer[self.subnet]["QoS Requirements"].append(ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["bound"]) # Log and store VLAN information logging.info(f"Intent with VLAN {vlan_value} realized\n") self.answer[self.subnet]["VLAN"] = vlan_value logging.info(tfs_request) with open(os.path.join(TEMPLATES_PATH, "l2vpn_request.json"), "w") as archivo: archivo.write(json.dumps(tfs_request,indent=2)) return tfs_request # def __tfs_l2vpn(self, ietf_intent): # """ # Translate slice intent into a TeraFlow service request. # # This method prepares a L2VPN service request by: # 1. Defining endpoint routers # 2. Loading a service template # 3. Generating a unique service UUID # 4. Configuring service endpoints # 5. Adding QoS constraints # 6. Preparing configuration rules for network interfaces # # Args: # ietf_intent (dict): IETF-formatted network slice intent. # # Returns: # dict: A TeraFlow service request for L2VPN configuration. # # """ # # Hardcoded router endpoints # # TODO (should be dynamically determined) # origin_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] # origin_router_if = 'eth2' # destination_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] # destination_router_if = 'eth2' # # # Extract QoS Profile from intent # QoSProfile = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["id"] # # vlan_value = 0 # # # Load L2VPN service template # self.__load_template(2, os.path.join(TEMPLATES_PATH, "ietfL2VPN_template")) # tfs_request = json.loads(str(self.__teraflow_template))["services"][0] # # # Generate unique service UUID # tfs_request["service_id"]["service_uuid"]["uuid"] += "-" + str(int(datetime.now().timestamp() * 1e7)) # # # Configure service endpoints # for endpoint in tfs_request["service_endpoint_ids"]: # endpoint["device_id"]["device_uuid"]["uuid"] = origin_router_id if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_id # endpoint["endpoint_uuid"]["uuid"] = origin_router_if if endpoint is tfs_request["service_endpoint_ids"][0] else destination_router_if # # self.answer[self.subnet]["QoS Requirements"] = [] # # Add service constraints # for i, constraint in enumerate(tfs_request["service_constraints"]): # bound = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["bound"] # self.answer[self.subnet]["QoS Requirements"].append(bound) # constraint["custom"]["constraint_value"] = str(bound) # # # Add configuration rules # for i, config_rule in enumerate(tfs_request["service_config"]["config_rules"][1:], start=1): # router_id = origin_router_id if i == 1 else destination_router_id # router_if = origin_router_if if i == 1 else destination_router_if # resource_value = config_rule["custom"]["resource_value"] # # sdp_index = i - 1 # vlan_value = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][sdp_index]["service-match-criteria"]["match-criterion"][0]["value"] # resource_value["vlan_id"] = int(vlan_value) # resource_value["circuit_id"] = vlan_value # resource_value["remote_router"] = destination_router_id if i == 1 else origin_router_id # resource_value["ni_name"] = 'ELAN{:s}'.format(str(vlan_value)) # config_rule["custom"]["resource_key"] = f"/device[{router_id}]/endpoint[{router_if}]/settings" # # # Log and store VLAN information # logging.info(f"Intent with VLAN {vlan_value} realized\n") # self.answer[self.subnet]["VLAN"] = vlan_value # return tfs_request # def __tfs_l2vpn_support(self, requests): """ Configuration support for L2VPN with path selection based on MPLS traffic-engineering tunnels Loading Loading @@ -1006,11 +1067,11 @@ class NSController: dict: A TeraFlow service request for L3VPN configuration. """ # Hardcoded router endpoints # TODO should be dynamically determined # TODO (should be dynamically determined) origin_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] origin_router_if = '0/0/0-GigabitEthernet0/0/0/0' destination_router_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][1]["attachment-circuits"]["attachment-circuit"][0]["sdp-peering"]["peer-sap-id"] destination_router_if = '0/0/3-GigabitEthernet0/0/0/3' destination_router_if = 'eth2' # Load L3VPN service template self.__load_template(2, os.path.join(TEMPLATES_PATH, "L3-VPN_template_empty.json")) Loading