Loading our-temp/ietf-l3vpn-service.json 0 → 100644 +83 −0 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} }]}}} } } ] } } ] } } } src/Constants.py +2 −2 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,7 +12,7 @@ # 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. # This file includes original contributions from Telefonica Innovación Digital S.L. import logging, os, json Loading src/helpers.py +28 −14 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 @@ -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( Loading @@ -30,20 +31,33 @@ class tfs_connector(): 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 def nbi_post(self, tfs_ip, service, path): Loading src/network_slice_controller.py +52 −51 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 Loading @@ -496,6 +496,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 @@ -826,7 +827,6 @@ 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. Loading @@ -849,26 +849,27 @@ 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/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 # 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 Loading Loading @@ -994,7 +995,7 @@ 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"] Loading src/templates/l2vpn_request.json 0 → 100644 +34 −0 Original line number Diff line number Diff line { "ietf-l2vpn-svc:vpn-service": [ { "vpn-id": "11327140-7361-41b3-aa45-e84a7fb40be9", "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-network-access": { "interface": { "ip-address": "10.60.11.3", "encapsulation": "ethernet" } } }, { "site-id": "3.3.3.3", "site-role": "spoke", "site-location": "AMF-N2", "site-network-access": { "interface": { "ip-address": "10.60.60.105", "encapsulation": "ethernet" } } } ] } ] } No newline at end of file Loading
our-temp/ietf-l3vpn-service.json 0 → 100644 +83 −0 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} }]}}} } } ] } } ] } } }
src/Constants.py +2 −2 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,7 +12,7 @@ # 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. # This file includes original contributions from Telefonica Innovación Digital S.L. import logging, os, json Loading
src/helpers.py +28 −14 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 @@ -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( Loading @@ -30,20 +31,33 @@ class tfs_connector(): 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 def nbi_post(self, tfs_ip, service, path): Loading
src/network_slice_controller.py +52 −51 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 Loading @@ -496,6 +496,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 @@ -826,7 +827,6 @@ 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. Loading @@ -849,26 +849,27 @@ 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/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 # 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 Loading Loading @@ -994,7 +995,7 @@ 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"] Loading
src/templates/l2vpn_request.json 0 → 100644 +34 −0 Original line number Diff line number Diff line { "ietf-l2vpn-svc:vpn-service": [ { "vpn-id": "11327140-7361-41b3-aa45-e84a7fb40be9", "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-network-access": { "interface": { "ip-address": "10.60.11.3", "encapsulation": "ethernet" } } }, { "site-id": "3.3.3.3", "site-role": "spoke", "site-location": "AMF-N2", "site-network-access": { "interface": { "ip-address": "10.60.60.105", "encapsulation": "ethernet" } } } ] } ] } No newline at end of file