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

Modify code to realize l2vpn slices depending on the flag (webUI or NBI)

parent 2d982365
Loading
Loading
Loading
Loading

our-temp/ietf-l3vpn-service.json

deleted100644 → 0
+0 −83
Original line number Diff line number Diff line
{
    "ietf-l3vpn-svc:l3vpn-svc": {
        "vpn-services": {"vpn-service": [{"vpn-id": "ietf-l3vpn-svc"}]},
        "sites": {
            "site": [
                {
                    "site-id": "site_DC1",
                    "management": {"type": "ietf-l3vpn-svc:provider-managed"},
                    "locations": {"location": [{"location-id": "DC1"}]},
                    "devices": {"device": [{"device-id": "dc1", "location": "DC1"}]},
                    "site-network-accesses": {
                        "site-network-access": [
                            {
                                "site-network-access-id": "eth1",
                                "site-network-access-type": "ietf-l3vpn-svc:multipoint",
                                "device-reference": "dc1",
                                "vpn-attachment": {"vpn-id": "ietf-l3vpn-svc", "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": 1000000000,
                                    "svc-output-bandwidth": 1000000000,
                                    "qos": {"qos-profile": {"classes": {"class": [{
                                        "class-id": "qos-realtime",
                                        "direction": "ietf-l3vpn-svc:both",
                                        "latency": {"latency-boundary": 10},
                                        "bandwidth": {"guaranteed-bw-percent": 100}
                                    }]}}}
                                }
                            }
                        ]
                    }
                },
                {
                    "site-id": "site_DC2",
                    "management": {"type": "ietf-l3vpn-svc:provider-managed"},
                    "locations": {"location": [{"location-id": "DC2"}]},
                    "devices": {"device": [{"device-id": "dc2", "location": "DC2"}]},
                    "site-network-accesses": {
                        "site-network-access": [
                            {
                                "site-network-access-id": "eth1",
                                "site-network-access-type": "ietf-l3vpn-svc:multipoint",
                                "device-reference": "dc2",
                                "vpn-attachment": {"vpn-id": "ietf-l3vpn-svc", "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": 1000000000,
                                    "svc-output-bandwidth": 1000000000,
                                    "qos": {"qos-profile": {"classes": {"class": [{
                                        "class-id": "qos-realtime",
                                        "direction": "ietf-l3vpn-svc:both",
                                        "latency": {"latency-boundary": 10},
                                        "bandwidth": {"guaranteed-bw-percent": 100}
                                    }]}}}
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
}
+10 −2
Original line number Diff line number Diff line
# 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.
@@ -12,6 +12,10 @@
# See the License for the specific language governing permissions and
# 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

@@ -44,12 +48,16 @@ PCE_EXTERNAL = False
# Controller Flags
# If True, config is not sent to controllers
DUMMY_MODE = False

#####TERAFLOW#####
# Teraflow IP
TFS_IP = ips.get('TFS_IP')
UPLOAD_TYPE = "NBI"  # "WEBUI" or "NBI"
UPLOAD_TYPE = "WEBUI"  # "WEBUI" or "NBI"
NBI_L2_PATH = "restconf/data/ietf-l2vpn-svc:l2vpn-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')

+14 −28
Original line number Diff line number Diff line
# 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.
@@ -17,7 +17,6 @@
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(
@@ -31,33 +30,20 @@ class tfs_connector():
        user="admin"
        password="admin"
        token=""
        #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
        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")}
        token={'csrf_token':token}
        response = requests.post(url_l2, headers=headers, data=json_decoded, timeout=60)
        logging.info(response.text)
        #logging.debug("Http response: %s",response.text)
        response = session.post(url,files=files,data=token,timeout=60)
        logging.debug("Http response: %s",response.text)
        return response

    def nbi_post(self, tfs_ip, service, path):
+69 −59
Original line number Diff line number Diff line
# 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.
@@ -302,7 +302,9 @@ class NSController:
                    if UPLOAD_TYPE == "WEBUI":
                        response = tfs_connector().webui_post(self.tfs_ip, requests)
                    elif UPLOAD_TYPE == "NBI":
                        response = tfs_connector().nbi_post(self.tfs_ip, requests, self.path)
                        for intent in requests["services"]:
                            # Send each separate NBI request
                            response = tfs_connector().nbi_post(self.tfs_ip, intent, self.path)

                            if not response.ok:
                                return self.__send_response(False, code=response.status_code, message=f"Teraflow upload failed. Response: {response.text}")
@@ -496,7 +498,6 @@ 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,
@@ -827,6 +828,7 @@ 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.
@@ -849,48 +851,17 @@ 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 = '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'

        # 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.__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"]
            # 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.__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
        
        # 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"]
@@ -902,7 +873,26 @@ class NSController:
                }
            }
            self.answer[self.subnet]["QoS Requirements"].append(service_constraint["custom"])
                    tfs_request["service_constraints"].append(service_constraint)
        
        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 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

            # 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):
@@ -919,9 +909,29 @@ class NSController:
                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
        elif UPLOAD_TYPE == "NBI":

            # 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))

            # Generate service UUID
            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

            # 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"]

        logging.info(f"L2VPN Intent realized\n")
                self.answer[self.subnet]["VLAN"] = vlan_value
        return tfs_request
    
    def __tfs_l2vpn_support(self, requests):
+7 −9
Original line number Diff line number Diff line
{
  "ietf-l2vpn-svc:vpn-service": [
    {
      "vpn-id": "11327140-7361-41b3-aa45-e84a7fb40be9",
      "vpn-id": "",
      "customer-name": "osm",
      "vpn-svc-type": "vpws",
      "svc-topo": "any-to-any",
      "site": [
        {
          "site-id": "1.1.1.1",
          "site-role": "hub",
          "site-location": "CU-N2",
          "site-id": "",
          "site-location": "",
          "site-network-access": {
            "interface": {
              "ip-address": "10.60.11.3",
              "ip-address": "",
              "encapsulation": "ethernet"
            }
          }
        },
        {
          "site-id": "3.3.3.3",
          "site-role": "spoke",
          "site-location": "AMF-N2",
          "site-id": "",
          "site-location": "",
          "site-network-access": {
            "interface": {
              "ip-address": "10.60.60.105",
              "ip-address": "",
              "encapsulation": "ethernet"
            }
          }
Loading