diff --git a/README.md b/README.md index e42cae615826530cc1c6dbb680590db534465746..6eda6c131858ced75e65fd492cf138755252adfe 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ The realizer module determines the realization of each slice by interacting with NSC Architecture ## Requirements -- Python3 +- Python3.12 - python3-pip - python3-venv @@ -70,8 +70,8 @@ To deploy and execute the NSC, follow these steps: 0. **Preparation** ``` - git clone https://github.com/Telefonica/network_slice_controller.git - cd network_slice_controller + git clone https://labs.etsi.org/rep/tfs/nsc.git + cd nsc python3 -m venv venv source venv/bin/activate pip install -r requirements.txt diff --git a/app.py b/app.py index 383d31c8e5c60fe498f87747fc6fc9ea104c68b1..1e34ec5486daeb0eb38f32e84494ba5f4fcb4db0 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,4 @@ -# 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,8 @@ # 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. + from flask import Flask from flask_restx import Api from flask_cors import CORS diff --git a/images/NSC_Architecture.png b/images/NSC_Architecture.png index adee5374846eccdbf1974e616890b55fc4c131e2..852437d55f3fadcb9c6a4303be7c70a264977e30 100644 Binary files a/images/NSC_Architecture.png and b/images/NSC_Architecture.png differ diff --git a/src/Constants.py b/src/Constants.py index f699aed96dbcfa94839baaf467b139e874fd3afb..337be8856e4a91907a76466498fb6cb57634bca9 100644 --- a/src/Constants.py +++ b/src/Constants.py @@ -1,4 +1,4 @@ -# 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,8 @@ # 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 diff --git a/src/helpers.py b/src/helpers.py index abbac026ba39edae5040b9e1efe8d6476d89caf5..47adc1619360e8f8438842b73efe31a07b3c1252 100644 --- a/src/helpers.py +++ b/src/helpers.py @@ -1,4 +1,4 @@ -# 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. diff --git a/src/network_slice_controller.py b/src/network_slice_controller.py index 871e5d19e14b0e118242a80986918248a9c35a57..7d141146d3a4cf1862008ef031ebfdfc10957f38 100644 --- a/src/network_slice_controller.py +++ b/src/network_slice_controller.py @@ -1,4 +1,4 @@ -# 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. diff --git a/src/old/intent_generator.py b/src/old/intent_generator.py deleted file mode 100644 index 05eae796f8a910477e0f66ce04d173a3541f5a24..0000000000000000000000000000000000000000 --- a/src/old/intent_generator.py +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env python3 -import json -import os -#import requests -import socket -import random - -# Obtain the current directory -current_dir = os.path.dirname(os.path.abspath(__file__)) - -# Create the path to the templates directory -parent_dir = os.path.dirname(current_dir) -file_path = os.path.join(parent_dir, "templates") - -class IntentCreator: - - def __init__(self, plt, lnodes): - self.plantilla = '' - with open(plt, 'r') as source: - self.plantilla = source.read() - self.nodes = '' - with open(lnodes, 'r') as source: - self.nodes = source.read() - self.nodes = json.loads(str(self.nodes)) - - - def __create_ep(self,node_name, node_ip, vlan_id): - ep_tansport_key = f"EpTransport {node_name}" - ep_transport = { - ep_tansport_key:{ - "IpAddress":str(node_ip.rsplit('.', 1)[0] + '.1'), - "logicalInterfaceInfo":{ - "logicalInterfaceType":"VLAN", - "logicalInterfaceId":vlan_id - }, - "NextHopInfo": str(node_ip.rsplit('.', 1)[0] + '.254'), - "qosProfile":"A", - "EpApplicationRef":[f"EP_F1U {node_name}"] - } - } - - ep_rp_key = f"EP_F1U {node_name}" - ep_rp = { - ep_rp_key:{ - "localAddress":node_ip, - "remoteAddress":"", - "epTransportRef":[ep_tansport_key] - } - } - return ep_transport,ep_rp - - def create_intent(self): - ''' - In this function we are going to create intents randomly and return them as a json. - Parameters to modify: - - Origin IP, Destination IP, Interfaces, Match type - - SDP id and Node id - - Required value - ''' - - request = json.loads(str(self.plantilla)) - - # Subnet list in the slicing environment - subnets = [["TopSliceSubnet1","TopSliceSubnetProfile"],["CNSliceSubnet1","CNSliceSubnetProfile"], ["RANSliceSubnet1","RANSliceSubnetProfile"], ["MidhaulSliceSubnet1","RANSliceSubnetProfile"], ["BackhaulSliceSubnet1","RANSliceSubnetProfile"]] - - # Possible values for throughput and latency - throughput = [100, 200, 500, 1000] - latency = [5, 10, 15, 20] - - # Assign random values to the QoS parameters - top_slice_qos = [random.choice(throughput), 0, random.choice(latency)] - top_slice_qos[1] = int(2*top_slice_qos[0]) - cn_slice_qos = [int(val) for val in [top_slice_qos[0]/2,top_slice_qos[1]/2, top_slice_qos[2]*0.4]] - ran_slice_qos = [int(val) for val in [top_slice_qos[0]/2,top_slice_qos[1]/2, top_slice_qos[2]*0.6]] - midhaul_slice_qos = [int(val) for val in [ran_slice_qos[0]*0.6,ran_slice_qos[1]*0.6, ran_slice_qos[2]/3]] - backhaul_slice_qos = [int(val) for val in [ran_slice_qos[0]*0.4,ran_slice_qos[1]*0.4, ran_slice_qos[2]*2/3]] - - qos = [top_slice_qos, cn_slice_qos, ran_slice_qos, midhaul_slice_qos, backhaul_slice_qos] - - for index, subnet in enumerate(subnets): - request[subnet[0]]["SliceProfileList"][0][subnet[1]]["dLThptPerSliceSubnet"]["GuaThpt"] = qos[index][0] - request[subnet[0]]["SliceProfileList"][0][subnet[1]]["dLThptPerSliceSubnet"]["MaxThpt"] = qos[index][1] - request[subnet[0]]["SliceProfileList"][0][subnet[1]]["uLThptPerSliceSubnet"]["GuaThpt"] = qos[index][0] - request[subnet[0]]["SliceProfileList"][0][subnet[1]]["uLThptPerSliceSubnet"]["MaxThpt"] = qos[index][1] - request[subnet[0]]["SliceProfileList"][0][subnet[1]]["dLLatency"] = qos[index][2] - request[subnet[0]]["SliceProfileList"][0][subnet[1]]["uLLatency"] = qos[index][2] - - #dus = self.nodes["public-prefixes"] - #cus = self.nodes["public-prefixes"] - dus = self.nodes["DU"] - cus = self.nodes["CU"] - - if random.randint(0, 1) == 0: - src = random.choice(dus) - dst = random.choice(cus) - else: - src = random.choice(cus) - dst = random.choice(dus) - - # Delete all the EpTransport objects from the request - pop_keys = [] - - for key in request: - # Verify if the key is an EpTransport object - if key.startswith("EpTransport") or key.startswith("EP_F1U"): - # Aggregate the key to the list of keys to be removed - pop_keys.append(key) - - for key in pop_keys: - request.pop(key) - - # Randomly select a VLAN ID - l_iid = ["100","200","300", "400"] - vlan_id = random.choice(l_iid) - - # Create the EPs for the source and destination nodes - ep_transport_src, ep_rp_src = self.__create_ep(src["node-name"], src["prefix"], vlan_id) - request.update(ep_transport_src) - request.update(ep_rp_src) - ep_transport_dst, ep_rp_dst = self.__create_ep(dst["node-name"], dst["prefix"], vlan_id) - request.update(ep_transport_dst) - request.update(ep_rp_dst) - - ep_transport_src_name = list(ep_transport_src.keys())[0] - ep_transport_dst_name = list(ep_transport_dst.keys())[0] - - request["MidhaulSliceSubnet1"]["EpTransport"] = [ep_transport_src_name, ep_transport_dst_name] - - request[ep_transport_src[ep_transport_src_name]["EpApplicationRef"][0]]["remoteAddress"] = request[ep_transport_dst[ep_transport_dst_name]["EpApplicationRef"][0]]["localAddress"] - request[ep_transport_dst[ep_transport_dst_name]["EpApplicationRef"][0]]["remoteAddress"] = request[ep_transport_src[ep_transport_src_name]["EpApplicationRef"][0]]["localAddress"] - - return (str(request).replace('\t', '').replace('\n', '').replace("'", '"').replace("None", '"None"')) - -if __name__ == '__main__': - creador = IntentCreator(os.path.join(file_path, "3gpp_template_empty.json"), os.path.join(file_path, "ips.json")) - print(creador.create_intent()) diff --git a/src/old/send_request.py b/src/old/send_request.py deleted file mode 100644 index dc55d57b9cb8a3eb134ecf82bc14f8389d7fe991..0000000000000000000000000000000000000000 --- a/src/old/send_request.py +++ /dev/null @@ -1,28 +0,0 @@ -import sys, os -import requests -import time -import json -from intent_generator import IntentCreator - -# Obtain the current directory -current_dir = os.path.dirname(os.path.abspath(__file__)) - -# Create the path to the templates directory -parent_dir = os.path.dirname(current_dir) -file_path = os.path.join(parent_dir, "templates") - -def send_request(port): - if len(sys.argv) == 2: - resp = IntentCreator(os.path.join(file_path, "3gpp_template_empty.json"), os.path.join(file_path, "ips.json")).create_intent() - requests.post(f'http://localhost:{port}/intent', headers={'Content-Type': 'application/json'}, data=resp) - else: - intent = sys.argv[2] - with open(intent, 'r') as source: - resp = source.read() - resp = str(resp).replace('\t', '').replace('\n', '').replace("'", '"').replace("None", '"None"') - requests.post(f'http://localhost:{port}/intent', headers={'Content-Type': 'application/json'}, data=resp) - -while True: - # Use: python3 send_request.py - send_request(sys.argv[1]) - time.sleep(3) diff --git a/swagger/models/create_models.py b/swagger/models/create_models.py index 3dac63b2b131e5e710f3a61b1e0b40f0f6d65336..94ca83bc53b978beb68512dd5959452375256f67 100644 --- a/swagger/models/create_models.py +++ b/swagger/models/create_models.py @@ -1,4 +1,4 @@ -# 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,8 @@ # 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. + from flask_restx import fields def create_gpp_nrm_28541_model(slice_ns): diff --git a/swagger/slice_namespace.py b/swagger/slice_namespace.py index 6585163a659b2eaa9db237a2c7a062ea8c4b29db..0f629f97e1a8ecbf4550f3e5a24e5ecca46c15ae 100644 --- a/swagger/slice_namespace.py +++ b/swagger/slice_namespace.py @@ -1,4 +1,4 @@ -# 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,8 @@ # 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. + from flask import request from flask_restx import Namespace, Resource, fields, reqparse from src.network_slice_controller import NSController