From c1b79b5e4a0d723e1c8a437c21bba6846b39a9c8 Mon Sep 17 00:00:00 2001 From: rahhal Date: Thu, 26 Jun 2025 13:20:09 +0000 Subject: [PATCH 1/3] =?UTF-8?q?Challenge=20#4=20=E2=80=93L3VPN=20Support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Constants.py | 9 +- src/helpers.py | 47 ++++--- src/network_slice_controller.py | 209 +++++++++++++++++++++----------- 3 files changed, 172 insertions(+), 93 deletions(-) diff --git a/src/Constants.py b/src/Constants.py index 337be88..c2e29ad 100644 --- a/src/Constants.py +++ b/src/Constants.py @@ -1,4 +1,4 @@ -# 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. @@ -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 @@ -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 diff --git a/src/helpers.py b/src/helpers.py index 47adc16..63918aa 100644 --- a/src/helpers.py +++ b/src/helpers.py @@ -1,4 +1,4 @@ -# 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. @@ -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( @@ -23,25 +24,39 @@ logging.basicConfig( #Teraflow class tfs_connector(): - - def simple_post(self, tfs_ip, service): + #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,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(' Date: Thu, 26 Jun 2025 13:20:09 +0000 Subject: [PATCH 2/3] =?UTF-8?q?Challenge=20#4=20=E2=80=93L3VPN=20Support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Constants.py | 7 ++++++- src/helpers.py | 3 ++- src/network_slice_controller.py | 34 +++++++++++++++++---------------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Constants.py b/src/Constants.py index b05deea..1cd84dd 100644 --- a/src/Constants.py +++ b/src/Constants.py @@ -1,4 +1,4 @@ -# 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. @@ -54,6 +54,11 @@ DUMMY_MODE = False TFS_IP = ips.get('TFS_IP') UPLOAD_TYPE = "WEBUI" # "WEBUI" or "NBI" NBI_L2_PATH = "restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services" +#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 diff --git a/src/helpers.py b/src/helpers.py index 0e15079..be052a8 100644 --- a/src/helpers.py +++ b/src/helpers.py @@ -1,4 +1,4 @@ -# 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. @@ -17,6 +17,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( diff --git a/src/network_slice_controller.py b/src/network_slice_controller.py index 34697b3..d64f955 100644 --- a/src/network_slice_controller.py +++ b/src/network_slice_controller.py @@ -1,4 +1,4 @@ -# 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. @@ -62,6 +62,8 @@ class NSController: self.controller_type = controller_type 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 @@ -498,6 +500,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, @@ -828,30 +831,29 @@ class NSController: logging.warning(f"Unsupported controller: {controller}. Defaulting to TFS L2VPN realization.") realizing_request = self.__tfs_l2vpn(ietf_intent) return realizing_request - def __tfs_l2vpn(self, ietf_intent): - """ - Translate slice intent into a TeraFlow service request. + """ + 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 + 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. + Args: + ietf_intent (dict): IETF-formatted network slice intent. - Returns: - dict: A TeraFlow service request for L2VPN configuration. + 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 = '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/0-GigabitEthernet0/0/0/0' -- GitLab From 8cef7eeb08caf1349d4f1cdd3b52b8d01132e151 Mon Sep 17 00:00:00 2001 From: velazquez Date: Wed, 27 Aug 2025 12:05:28 +0200 Subject: [PATCH 3/3] Modify code to realize l3vpn slices depending on the flag (webUI or NBI) --- src/Constants.py | 9 +- src/helpers.py | 5 +- src/network_slice_controller.py | 285 ++++++++++++-------- src/templates/ietfL3VPN_template_empty.json | 83 ++++++ 4 files changed, 263 insertions(+), 119 deletions(-) create mode 100644 src/templates/ietfL3VPN_template_empty.json diff --git a/src/Constants.py b/src/Constants.py index 2603e13..3b02ffd 100644 --- a/src/Constants.py +++ b/src/Constants.py @@ -1,5 +1,4 @@ -# Copyright 2025 Telefonica Innovación Digital S.L. -# Copyright 2025 Telefonica Innovación Digital S.L. +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -55,11 +54,7 @@ DUMMY_MODE = False TFS_IP = ips.get('TFS_IP') UPLOAD_TYPE = "WEBUI" # "WEBUI" or "NBI" NBI_L2_PATH = "restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services" -#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 +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 diff --git a/src/helpers.py b/src/helpers.py index 04fc838..0e15079 100644 --- a/src/helpers.py +++ b/src/helpers.py @@ -1,5 +1,4 @@ -# Copyright 2025 Telefonica Innovación Digital S.L. -# Copyright 2025 Telefonica Innovación Digital S.L. +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,8 +17,6 @@ import logging, requests, json from netmiko import ConnectHandler from src.Constants import DEFAULT_LOGGING_LEVEL -from requests.auth import HTTPBasicAuth -from requests.auth import HTTPBasicAuth # Configure logging to provide clear and informative log messages logging.basicConfig( diff --git a/src/network_slice_controller.py b/src/network_slice_controller.py index 204c198..6ac7088 100644 --- a/src/network_slice_controller.py +++ b/src/network_slice_controller.py @@ -1,5 +1,4 @@ -# Copyright 2025 Telefonica Innovación Digital S.L. -# Copyright 2025 Telefonica Innovación Digital S.L. +# Copyright 2022-2025 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,7 +17,7 @@ import json, time, os, logging, uuid, traceback, sys from datetime import datetime from src.helpers import tfs_connector, cisco_connector -from src.Constants import DEFAULT_LOGGING_LEVEL, TFS_IP, TFS_L2VPN_SUPPORT, IXIA_IP, SRC_PATH, TEMPLATES_PATH, DUMMY_MODE, DUMP_TEMPLATES, PLANNER_ENABLED, NRP_ENABLED, UPLOAD_TYPE, NBI_L2_PATH +from src.Constants import DEFAULT_LOGGING_LEVEL, TFS_IP, TFS_L2VPN_SUPPORT, IXIA_IP, SRC_PATH, TEMPLATES_PATH, DUMMY_MODE, DUMP_TEMPLATES, PLANNER_ENABLED, NRP_ENABLED, UPLOAD_TYPE, NBI_L2_PATH, NBI_L3_PATH from src.realizers.ixia.NEII_V4 import NEII_controller from src.planner.planner import Planner @@ -43,7 +42,7 @@ class NSController: - Slice Realization: Convert intents to specific network configurations (L2VPN, L3VPN) """ - def __init__(self, controller_type = "TFS", tfs_ip=TFS_IP, ixia_ip =IXIA_IP, need_l2vpn_support=TFS_L2VPN_SUPPORT, path=NBI_L2_PATH): + def __init__(self, controller_type = "TFS", tfs_ip=TFS_IP, ixia_ip =IXIA_IP, need_l2vpn_support=TFS_L2VPN_SUPPORT): """ Initialize the Network Slice Controller. @@ -62,12 +61,7 @@ class NSController: """ self.controller_type = controller_type self.tfs_ip = tfs_ip - self.path = path - self.path_l3 = path_l3 - #self.json_file = json_file - self.path = path - self.path_l3 = path_l3 - #self.json_file = json_file + self.path = "" self.answer = {} self.cool_answer = {} self.start_time = 0 @@ -448,6 +442,7 @@ class NSController: else: # Select slice service method way = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["service-tags"]["tag-type"]["value"] + way = "L3VPN" return self.__select_way(controller=self.controller_type, way=way, ietf_intent=ietf_intent) ### Generic functionalities @@ -504,8 +499,6 @@ class NSController: "setup_time": self.setup_time } # Add slice details to the response - logging.info(self.answer) - logging.info(self.answer) for subnet in self.answer: slice_info = { "id": subnet, @@ -647,8 +640,8 @@ class NSController: metrics = { - ("uLThptPerSliceSubnet", "MaxThpt"): ("throughput", "kbps"), - ("uLLatency",): ("latency", "ms"), + ("uLThptPerSliceSubnet", "MaxThpt"): ("one-way-bandwidth", "kbps"), + ("uLLatency",): ("one-way-delay-maximum", "milliseconds"), ("EnergyConsumption",): ("energy_consumption", "Joules"), ("EnergyEfficiency",): ("energy_efficiency", "W/bps"), ("CarbonEmissions",): ("carbon_emission", "gCO2eq"), @@ -836,43 +829,30 @@ class NSController: logging.warning(f"Unsupported controller: {controller}. Defaulting to TFS L2VPN realization.") realizing_request = self.__tfs_l2vpn(ietf_intent) return realizing_request + def __tfs_l2vpn(self, ietf_intent): - """ - Translate slice intent into a TeraFlow service request. - """ - 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 - 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. - Args: - ietf_intent (dict): IETF-formatted network slice intent. - - Returns: - dict: A TeraFlow service request for L2VPN configuration. - Returns: - dict: A TeraFlow service request for L2VPN configuration. + """ + 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' - origin_router_if = 'eth2' + 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/0-GigabitEthernet0/0/0/0' @@ -881,20 +861,34 @@ class NSController: vlan_value = 0 self.answer[self.subnet]["QoS Requirements"] = [] - + # Populate response with QoS requirements and VLAN from intent - for i, constraint in enumerate(ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"]): - bound = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["bound"] - metric_type = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["metric-type"] - metric_unit = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["metric-unit"] - service_constraint ={ - "custom": { - "constraint_type": f"{metric_type}[{metric_unit}]", - "constraint_value": f"{bound}" - } - } - self.answer[self.subnet]["QoS Requirements"].append(service_constraint["custom"]) - + slo_policy = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"] + + # Process metrics + for metric in slo_policy.get("metric-bound", []): + constraint_type = f"{metric['metric-type']}[{metric['metric-unit']}]" + constraint_value = str(metric["bound"]) + self.answer[self.subnet]["QoS Requirements"].append({ + "constraint_type": constraint_type, + "constraint_value": constraint_value + }) + + # Availability + if "availability" in slo_policy: + self.answer[self.subnet]["QoS Requirements"].append({ + "constraint_type": "availability[%]", + "constraint_value": str(slo_policy["availability"]) + }) + + # MTU + if "mtu" in slo_policy: + self.answer[self.subnet]["QoS Requirements"].append({ + "constraint_type": "mtu[bytes]", + "constraint_value": str(slo_policy["mtu"]) + }) + + # VLAN vlan_value = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["service-match-criteria"]["match-criterion"][0]["value"] self.answer[self.subnet]["VLAN"] = vlan_value @@ -931,7 +925,7 @@ class NSController: config_rule["custom"]["resource_key"] = f"/device[{router_id}]/endpoint[{router_if}]/settings" elif UPLOAD_TYPE == "NBI": - + self.path = NBI_L2_PATH # Load IETF L2VPN service template self.__load_template(2, os.path.join(TEMPLATES_PATH, "ietfL2VPN_template_empty.json")) tfs_request = json.loads(str(self.__teraflow_template)) @@ -942,15 +936,16 @@ class NSController: tfs_request["ietf-l2vpn-svc:vpn-service"][0]["vpn-id"] = uuid_only # Configure service endpoints - 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"] + sites = tfs_request["ietf-l2vpn-svc:vpn-service"][0]["site"] + sdps = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"] + + for i, site in enumerate(sites): + is_origin = (i == 0) + router_id = origin_router_id if is_origin else destination_router_id + sdp = sdps[0] if is_origin else sdps[1] + site["site-id"] = router_id + site["site-location"] = sdp["node-id"] + site["site-network-access"]["interface"]["ip-address"] = sdp["sdp-ip-address"] logging.info(f"L2VPN Intent realized\n") return tfs_request @@ -1049,49 +1044,123 @@ class NSController: 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/0-GigabitEthernet0/0/0/0' - - # Load L3VPN service template - self.__load_template(2, os.path.join(TEMPLATES_PATH, "L3-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 + # 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 self.answer[self.subnet]["QoS Requirements"] = [] - # Add service constraints - for i, constraint in enumerate(ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"]): - bound = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["bound"] - metric_type = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["metric-type"] - metric_unit = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"]["metric-bound"][i]["metric-unit"] - service_constraint ={ - "custom": { - "constraint_type": f"{metric_type}[{metric_unit}]", - "constraint_value": f"{bound}" - } - } - self.answer[self.subnet]["QoS Requirements"].append(service_constraint["custom"]) - tfs_request["service_constraints"].append(service_constraint) - - # 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["router_id"] = destination_router_id if i == 1 else origin_router_id - resource_value["vlan_id"] = int(vlan_value) - resource_value["address_ip"] = destination_router_id if i == 1 else origin_router_id - resource_value["policy_AZ"] = "policyA" - resource_value["policy_ZA"] = "policyB" - resource_value["ni_name"] = 'ELAN{:s}'.format(str(vlan_value)) - config_rule["custom"]["resource_key"] = f"/device[{router_id}]/endpoint[{router_if}]/settings" + + # Populate response with QoS requirements and VLAN from intent + slo_policy = ietf_intent["ietf-network-slice-service:network-slice-services"]["slo-sle-templates"]["slo-sle-template"][0]["slo-policy"] + + # Process metrics + for metric in slo_policy.get("metric-bound", []): + constraint_type = f"{metric['metric-type']}[{metric['metric-unit']}]" + constraint_value = str(metric["bound"]) + self.answer[self.subnet]["QoS Requirements"].append({ + "constraint_type": constraint_type, + "constraint_value": constraint_value + }) + + # Availability + if "availability" in slo_policy: + self.answer[self.subnet]["QoS Requirements"].append({ + "constraint_type": "availability[%]", + "constraint_value": str(slo_policy["availability"]) + }) + + # MTU + if "mtu" in slo_policy: + self.answer[self.subnet]["QoS Requirements"].append({ + "constraint_type": "mtu[bytes]", + "constraint_value": str(slo_policy["mtu"]) + }) + + # VLAN + vlan_value = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][0]["service-match-criteria"]["match-criterion"][0]["value"] + self.answer[self.subnet]["VLAN"] = vlan_value + + if UPLOAD_TYPE == "WEBUI": + # Load L3VPN service template + self.__load_template(2, os.path.join(TEMPLATES_PATH, "L3-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 + + # Add service constraints + for constraint in self.answer[self.subnet]["QoS Requirements"]: + tfs_request["service_constraints"].append({"custom": constraint}) + + # 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["router_id"] = destination_router_id if i == 1 else origin_router_id + resource_value["vlan_id"] = int(vlan_value) + resource_value["address_ip"] = destination_router_id if i == 1 else origin_router_id + resource_value["policy_AZ"] = "policyA" + resource_value["policy_ZA"] = "policyB" + resource_value["ni_name"] = 'ELAN{:s}'.format(str(vlan_value)) + config_rule["custom"]["resource_key"] = f"/device[{router_id}]/endpoint[{router_if}]/settings" + + elif UPLOAD_TYPE == "NBI": + self.path = NBI_L3_PATH + # Load IETF L3VPN service template + self.__load_template(2, os.path.join(TEMPLATES_PATH, "ietfL3VPN_template_empty.json")) + tfs_request = json.loads(str(self.__teraflow_template)) + + # Generate service UUID + full_id = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["id"] + tfs_request["ietf-l3vpn-svc:l3vpn-svc"]["vpn-services"]["vpn-service"][0]["vpn-id"] = full_id + # Configure service endpoints + for i, site in enumerate(tfs_request["ietf-l3vpn-svc:l3vpn-svc"]["sites"]["site"]): + + # Determine if origin or destination + is_origin = (i == 0) + sdp_index = 0 if is_origin else 1 + location = ietf_intent["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["sdps"]["sdp"][sdp_index]["node-id"] + router_id = origin_router_id if is_origin else destination_router_id + router_if = origin_router_if if is_origin else destination_router_if + + # Assign common values + site["site-id"] = f"site_{location}" + site["locations"]["location"][0]["location-id"] = location + site["devices"]["device"][0]["device-id"] = router_id + site["devices"]["device"][0]["location"] = location + + access = site["site-network-accesses"]["site-network-access"][0] + access["site-network-access-id"] = router_if + access["device-reference"] = router_id + access["vpn-attachment"]["vpn-id"] = full_id + + # Aplicar restricciones QoS + for constraint in self.answer[self.subnet]["QoS Requirements"]: + ctype = constraint["constraint_type"] + cvalue = float(constraint["constraint_value"]) + if constraint["constraint_type"].startswith("one-way-bandwidth"): + unit = constraint["constraint_type"].split("[")[-1].rstrip("]") + multiplier = {"bps": 1, "kbps": 1_000, "Mbps": 1_000_000, "Gbps": 1_000_000_000}.get(unit, 1) + value = int(cvalue * multiplier) + access["service"]["svc-input-bandwidth"] = value + access["service"]["svc-output-bandwidth"] = value + elif ctype == "one-way-delay-maximum[milliseconds]": + access["service"]["qos"]["qos-profile"]["classes"]["class"][0]["latency"]["latency-boundary"] = int(cvalue) + elif ctype == "availability[%]": + access["service"]["qos"]["qos-profile"]["classes"]["class"][0]["bandwidth"]["guaranteed-bw-percent"] = int(cvalue) + elif ctype == "mtu[bytes]": + access["service"]["svc-mtu"] = int(cvalue) + logging.info(f"L3VPN Intent realized\n") self.answer[self.subnet]["VLAN"] = vlan_value diff --git a/src/templates/ietfL3VPN_template_empty.json b/src/templates/ietfL3VPN_template_empty.json new file mode 100644 index 0000000..628abcb --- /dev/null +++ b/src/templates/ietfL3VPN_template_empty.json @@ -0,0 +1,83 @@ +{ + "ietf-l3vpn-svc:l3vpn-svc": { + "vpn-services": {"vpn-service": [{"vpn-id": ""}]}, + "sites": { + "site": [ + { + "site-id": "", + "management": {"type": "ietf-l3vpn-svc:provider-managed"}, + "locations": {"location": [{"location-id": ""}]}, + "devices": {"device": [{"device-id": "", "location": ""}]}, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "", + "vpn-attachment": {"vpn-id": "", "site-role": "ietf-l3vpn-svc:spoke-role"}, + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "192.168.1.1", + "customer-address": "192.168.1.10", + "prefix-length": 24 + } + } + }, + "service": { + "svc-mtu": 1500, + "svc-input-bandwidth": 1, + "svc-output-bandwidth": 1, + "qos": {"qos-profile": {"classes": {"class": [{ + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": {"latency-boundary": 100}, + "bandwidth": {"guaranteed-bw-percent": 100} + }]}}} + } + } + ] + } + }, + { + "site-id": "", + "management": {"type": "ietf-l3vpn-svc:provider-managed"}, + "locations": {"location": [{"location-id": ""}]}, + "devices": {"device": [{"device-id": "", "location": ""}]}, + "site-network-accesses": { + "site-network-access": [ + { + "site-network-access-id": "", + "site-network-access-type": "ietf-l3vpn-svc:multipoint", + "device-reference": "", + "vpn-attachment": {"vpn-id": "", "site-role": "ietf-l3vpn-svc:hub-role"}, + "ip-connection": { + "ipv4": { + "address-allocation-type": "ietf-l3vpn-svc:static-address", + "addresses": { + "provider-address": "192.168.2.1", + "customer-address": "192.168.2.10", + "prefix-length": 24 + } + } + }, + "service": { + "svc-mtu": 1500, + "svc-input-bandwidth": 1, + "svc-output-bandwidth": 1, + "qos": {"qos-profile": {"classes": {"class": [{ + "class-id": "qos-realtime", + "direction": "ietf-l3vpn-svc:both", + "latency": {"latency-boundary": 100}, + "bandwidth": {"guaranteed-bw-percent": 100} + }]}}} + } + } + ] + } + } + ] + } + } +} -- GitLab