Commit 1c9c24fe authored by Pablo Armingol's avatar Pablo Armingol
Browse files

Refactor logging setup in app.py and enhance NSController with improved...

Refactor logging setup in app.py and enhance NSController with improved logging and rule generation handling
parent 910cc1dd
Loading
Loading
Loading
Loading
+24 −20
Original line number Diff line number Diff line
# 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.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# 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
from flask import Flask
from flask_restx import Api
from flask_cors import CORS
from swagger.slice_namespace import slice_ns
from src.constants import NSC_PORT

# Configuración de logging básica para fichero
logging.basicConfig(
    filename='nsc_controller.log',
    level=logging.INFO,
    format='%(asctime)s %(levelname)s %(name)s: %(message)s',
    filemode='w'
)

# Obtener logger raíz y logger Flask
logger = logging.getLogger()
flask_logger = logging.getLogger('werkzeug')  # logger que usa Flask para accesos HTTP

# Añadir handler de fichero a logger de Flask para asegurarnos que escribe en el log
file_handler = logging.FileHandler('nsc_controller.log')
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s: %(message)s')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
flask_logger.addHandler(file_handler)

app = Flask(__name__)
CORS(app)

# Create API instance
api = Api(
    app,
    version="1.0",
    title="Network Slice Controller (NSC) API",
    description="API for orchestrating and realizing transport network slice requests",
    doc="/nsc"  # Swagger UI URL
    doc="/nsc"
)

# Register namespaces
api.add_namespace(slice_ns, path="/slice")

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=NSC_PORT, debug=True)
    logger.info("Running  Network Slice Controller in %s:%s", "192.168.202.50", NSC_PORT)
    app.run(host="192.168.202.50", port=NSC_PORT, debug=True)
+31 −19
Original line number Diff line number Diff line
@@ -180,10 +180,20 @@ def send_network_slice_request(data: str, action: str = "create") -> dict:
        print(f"Request failed with status code {response.status_code}: {response.text}")
        response.raise_for_status()

def group_block(group, action, group_id_override=None):
def group_block(group, action, group_id_override=None, node = None):
    active   = "true" if action == 'create' else "false"
    group_id = group_id_override if group_id_override is not None else group["digital_sub_carriers_group_id"]

    if node == "leaf":
        return {
            "digital_sub_carriers_group_id": group_id,
            "digital_sub_carrier_id": [
                {'sub_carrier_id': 1, 'active': active}, 
                {'sub_carrier_id': 2, 'active': active}, 
                {'sub_carrier_id': 3, 'active': active},
                {'sub_carrier_id': 4, 'active': active}
                ]
            }
    else:
        return {
            "digital_sub_carriers_group_id": group_id,
            "digital_sub_carrier_id": [
@@ -234,25 +244,24 @@ def generate_rules(connectivity_service, intent, action):
        if dest == "T1.1": 
            name = "channel-1"
            freq = 195006250
        elif dest == "T1.2": 
        if dest == "T1.2": 
            name =   "channel-3"
            freq =   195018750
        else: 
        if dest == "T1.3": 
            name = "channel-5"
            freq = 195031250

        leaf = {
            "name": name,
            "frequency": freq,
            "target_output_power": group["Tx-power"],
            "operational_mode": int(group["operational-mode"]),
            "operation" : "merge",
            "digital_sub_carriers_group": [group_block(group, action, group_id_override=1)]
            "digital_sub_carriers_group": [group_block(group, action, group_id_override=1, node = "leaf")]
        }

        leaves.append(leaf)

    final_json = {"components": [hub] + leaves}

    if action == 'create':
        provisionamiento["actions"].append({
            "type":   "XR_AGENT_ACTIVATE_TRANSCEIVER",
@@ -270,8 +279,7 @@ def generate_rules(connectivity_service, intent, action):
            for ac in attachments:
                ip = ac.get('ac-ipv4-address', None)
                prefix = ac.get('ac-ipv4-prefix-length', None)
                last_octet = int(ip.split('.')[-1]) if ip else 0
                vlan = 100 + last_octet
                vlan = 500
                nodes[node] = {
                    "ip-address": ip,
                    "ip-mask":    prefix,
@@ -294,7 +302,11 @@ def generate_rules(connectivity_service, intent, action):
                "dest2-node-uuid":  dest_list[1],
                "dest2-ip-address": nodes[dest_list[1]]["ip-address"],
                "dest2-ip-mask":    str(nodes[dest_list[1]]["ip-mask"]),
                "dest2-vlan-id":    nodes[dest_list[1]]["vlan-id"]
                "dest2-vlan-id":    nodes[dest_list[1]]["vlan-id"],
                "dest3-node-uuid":  dest_list[2],
                "dest3-ip-address": nodes[dest_list[2]]["ip-address"],
                "dest3-ip-mask":    str(nodes[dest_list[2]]["ip-mask"]),
                "dest3-vlan-id":    nodes[dest_list[2]]["vlan-id"]
        },
            "controller-uuid": "IP Controller"
        })
+22 −5
Original line number Diff line number Diff line
@@ -236,6 +236,7 @@ class NSController:
                logging.info("DELETE REQUEST RECEIVED: %s", intent_json)

                rules = self.__planner(intent_json ,action = action)
                logging.info(f"Rules generated by planner: \n {rules}")
                tfs_request = self.__realizer(rules=rules)
            else:
                self.start_time = time.perf_counter()
@@ -538,7 +539,7 @@ class NSController:
            content = json.load(file)

        # Determine the actual ID to use
        slice_id = service_uuid if service_uuid is not None else slice_id
        slice_id = "T2.1_to_T1.1,T1.2,T1.3"
        if slice_id is None:
            # If neither given, fallback to intent's internal id
            slice_id = intent["ietf-network-slice-service"]["slice_id"] if "slice_id" in intent["ietf-network-slice-service"] \
@@ -738,13 +739,14 @@ class NSController:
        return True, score  # Si pasó todas las verificaciones, la NRP es viable

    def __planner(self, intent, action,  nrp_view= None):

        if action == 'delete':
            logging.info("DELETE REQUEST RECEIVED: %s", intent)
            with open(os.path.join(SRC_PATH, "slice_ddbb.json"), 'r', encoding='utf-8') as file:
                slices = json.load(file) 

            for slice_obj in slices:
                if 'slice_id' in slice_obj and slice_obj['slice_id'] == intent:
                    logging.info("Slice found: %s", slice_obj['slice_id'])
                    source = None
                    destination = None
                    services = slice_obj['intent']['ietf-network-slice-service:network-slice-services']['slice-service']
@@ -789,6 +791,7 @@ class NSController:
                "destination": destination,
                "connectivity-service": response
            }
            logging.info(summary)
            rules = generate_rules(summary, intent,action)
        return rules

@@ -818,6 +821,7 @@ class NSController:
            "band": 200,
            "subcarriers_per_source": [4] * len(sources_list)
        }
        logging.info(f"Payload for path computation: {json.dumps(payload, indent=2)}")

        response = requests.post(url, headers=headers, data=json.dumps(payload))
        return json.loads(response.text)
@@ -964,6 +968,7 @@ class NSController:
            dict: A TeraFlow service request for deleting the optical slice.
        """
        transceiver_params = []
        response = None
        for rule in rules["actions"]:
            if rule["type"] == "DEACTIVATE_TRANSCEIVER":
                params = {
@@ -994,13 +999,13 @@ class NSController:
                    'dst_router_tp' : transceiver_params[1]["router_tp"],
                }
                url = f'http://10.95.86.58/restconf/E2E/v1/service/ipowdm={rule["content"]["tunnel-uuid"]}:{data}'
                response = requests.delete(url, timeout=10)
                response = requests.delete(url, timeout=30)
                logging.info("Response: %s", response)

            elif rule["type"] == "DEACTIVATE_XR_AGENT_TRANSCEIVER":
                logging.info("Sending DELETE XR AGENT service request to Orchestrator")
                url = f'http://10.95.89.50/restconf/E2E/v1/service/ipowdm={rule["uuid"]}[[{rule["nodes"]}]]:{json.dumps(rule["content"])}'
                response = requests.delete(url, timeout=10)
                response = requests.delete(url, timeout=30)
                logging.info("Response: %s", response)

    def __optic_slice(self, ietf_intent, rules=None):
@@ -1192,8 +1197,13 @@ class NSController:
                dst2_ip_address = rule["content"]["dest2-ip-address"]
                dst2_ip_mask    = rule["content"]["dest2-ip-mask"]
                dst2_vlan_id    = rule["content"]["dest2-vlan-id"]
                dst3_router_id  = rule["content"]["dest3-node-uuid"]
                dst3_ip_address = rule["content"]["dest3-ip-address"]
                dst3_ip_mask    = rule["content"]["dest3-ip-mask"]
                dst3_vlan_id    = rule["content"]["dest3-vlan-id"]


                service_uuid = rule["content"]["tunnel-uuid"] + '-' + src_router_id + '-' + dst1_router_id + '-' + dst2_router_id
                service_uuid = rule["content"]["tunnel-uuid"] + '-' + src_router_id + '-' + dst1_router_id + '-' + dst2_router_id+ '-' + dst3_router_id
                self.__load_template(2, os.path.join(TEMPLATES_PATH, "IPoWDM_orchestrator.json"))
                tfs_request = json.loads(str(self.__teraflow_template))
                tfs_request["services"][0]["service_id"]["service_uuid"]["uuid"] = service_uuid
@@ -1220,6 +1230,13 @@ class NSController:
                    'ip_mask': dst2_ip_mask,
                    'vlan_id': dst2_vlan_id
                })
                dst.append({
                    'uuid': dst3_router_id,
                    'ip_address': dst3_ip_address,
                    'ip_mask': dst3_ip_mask,
                    'vlan_id': dst3_vlan_id
                })

                config_rules["ipowdm"]["rule_set"]["dst"]  = dst
                bandwidth = rule.get("bandwidth", 0) 
                config_rules["ipowdm"]["rule_set"]["bw"]   = bandwidth