# Copyright 2022-2024 ETSI OSG/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. import os import socket from flask import Flask, request from YangValidator import YangValidator app = Flask(__name__) # Retrieve the IP address of the current machine current_ip = socket.gethostbyname(socket.gethostname()) yang_validator = YangValidator('etsi-qkd-sdn-node', ['etsi-qkd-node-types']) # Update IP address with the current machine's IP nodes = { f'{current_ip}:11111': {'node': { 'qkdn_id': '00000001-0000-0000-0000-000000000000', }, 'qkdn_capabilities': { }, 'qkd_applications': { 'qkd_app': [ { 'app_id': '00000001-0001-0000-0000-000000000000', 'client_app_id': [], 'app_statistics': { 'statistics': [] }, 'app_qos': { }, 'backing_qkdl_id': [] } ] }, 'qkd_interfaces': { 'qkd_interface': [ { 'qkdi_id': '100', 'qkdi_att_point': { }, 'qkdi_capabilities': { } }, { 'qkdi_id': '101', 'qkdi_att_point': { 'device': current_ip, 'port': '1001' }, 'qkdi_capabilities': { } } ] }, 'qkd_links': { 'qkd_link': [] } }, f'{current_ip}:22222': {'node': { 'qkdn_id': '00000002-0000-0000-0000-000000000000', }, 'qkdn_capabilities': { }, 'qkd_applications': { 'qkd_app': [ { 'app_id': '00000002-0001-0000-0000-000000000000', 'client_app_id': [], 'app_statistics': { 'statistics': [] }, 'app_qos': { }, 'backing_qkdl_id': [] } ] }, 'qkd_interfaces': { 'qkd_interface': [ { 'qkdi_id': '200', 'qkdi_att_point': { }, 'qkdi_capabilities': { } }, { 'qkdi_id': '201', 'qkdi_att_point': { 'device': current_ip, 'port': '2001' }, 'qkdi_capabilities': { } }, { 'qkdi_id': '202', 'qkdi_att_point': { 'device': current_ip, 'port': '2002' }, 'qkdi_capabilities': { } } ] }, 'qkd_links': { 'qkd_link': [] } }, f'{current_ip}:33333': {'node': { 'qkdn_id': '00000003-0000-0000-0000-000000000000', }, 'qkdn_capabilities': { }, 'qkd_applications': { 'qkd_app': [ { 'app_id': '00000003-0001-0000-0000-000000000000', 'client_app_id': [], 'app_statistics': { 'statistics': [] }, 'app_qos': { }, 'backing_qkdl_id': [] } ] }, 'qkd_interfaces': { 'qkd_interface': [ { 'qkdi_id': '300', 'qkdi_att_point': { }, 'qkdi_capabilities': { } }, { 'qkdi_id': '301', 'qkdi_att_point': { 'device': current_ip, 'port': '3001' }, 'qkdi_capabilities': { } } ] }, 'qkd_links': { 'qkd_link': [] } } } def get_side_effect(url): steps = url.lstrip('https://').lstrip('http://').rstrip('/') parts = steps.split('/') # Ensure there are enough parts to unpack if len(parts) < 4: raise ValueError(f"Expected at least 4 parts in the URL, got {len(parts)}: {steps}") ip_port, _, _, header, *steps = parts header_splitted = header.split(':') module = header_splitted[0] assert(module == 'etsi-qkd-sdn-node') if ip_port.startswith('127.0.0.1'): ip_port = ip_port.replace('127.0.0.1', current_ip) tree = {'qkd_node': nodes[ip_port]['node'].copy()} if len(header_splitted) == 1 or not header_splitted[1]: value = nodes[ip_port].copy() value.pop('node') tree['qkd_node'].update(value) return tree, tree root = header_splitted[1] assert(root == 'qkd_node') if not steps: return tree, tree endpoint, *steps = steps value = nodes[ip_port][endpoint] if not steps: return_value = {endpoint: value} tree['qkd_node'].update(return_value) return return_value, tree raise Exception('Url too long') def edit(from_dict, to_dict, create): for key, value in from_dict.items(): if isinstance(value, dict): if key not in to_dict and create: to_dict[key] = {} edit(from_dict[key], to_dict[key], create) elif isinstance(value, list): to_dict[key].extend(value) else: to_dict[key] = value def edit_side_effect(url, json, create): steps = url.lstrip('https://').lstrip('http://').rstrip('/') parts = steps.split('/') # Ensure there are enough parts to unpack if len(parts) < 4: raise ValueError(f"Expected at least 4 parts in the URL, got {len(parts)}: {steps}") ip_port, _, _, header, *steps = parts module, root = header.split(':') assert(module == 'etsi-qkd-sdn-node') assert(root == 'qkd_node') if not steps: edit(json, nodes[ip_port]['node'], create) return endpoint, *steps = steps if not steps: edit(json[endpoint], nodes[ip_port][endpoint], create) return raise Exception('Url too long') @app.get('/', defaults={'path': ''}) @app.get("/") @app.get('/') def get(path): try: msg, msg_validate = get_side_effect(request.base_url) print(msg_validate) yang_validator.parse_to_dict(msg_validate) return msg except ValueError as e: return {'error': str(e)}, 400 @app.post('/', defaults={'path': ''}) @app.post("/") @app.post('/') def post(path): success = True reason = '' try: edit_side_effect(request.base_url, request.json, True) except Exception as e: reason = str(e) success = False return {'success': success, 'reason': reason} @app.route('/', defaults={'path': ''}, methods=['PUT', 'PATCH']) @app.route("/", methods=['PUT', 'PATCH']) @app.route('/', methods=['PUT', 'PATCH']) def patch(path): success = True reason = '' try: edit_side_effect(request.base_url, request.json, False) except Exception as e: reason = str(e) success = False return {'success': success, 'reason': reason} # import json # from mock import requests # import pyangbind.lib.pybindJSON as enc # from pyangbind.lib.serialise import pybindJSONDecoder as dec # from yang.sbi.qkd.templates.etsi_qkd_sdn_node import etsi_qkd_sdn_node # module = etsi_qkd_sdn_node() # url = 'https://1.1.1.1/restconf/data/etsi-qkd-sdn-node:' # # Get node all info # z = requests.get(url).json() # var = dec.load_json(z, None, None, obj=module) # print(enc.dumps(var)) # Reset module variable because it is already filled # module = etsi_qkd_sdn_node() # # Get node basic info # node = module.qkd_node # z = requests.get(url + 'qkd_node').json() # var = dec.load_json(z, None, None, obj=node) # print(enc.dumps(var)) # # Get all apps # apps = node.qkd_applications # z = requests.get(url + 'qkd_node/qkd_applications').json() # var = dec.load_json(z, None, None, obj=apps) # print(enc.dumps(var)) # # Edit app 0 # app = apps.qkd_app['00000000-0001-0000-0000-000000000000'] # app.client_app_id = 'id_0' # requests.put(url + 'qkd_node/qkd_applications/qkd_app=00000000-0001-0000-0000-000000000000', json=json.loads(enc.dumps(app))) # # Create app 1 # app = apps.qkd_app.add('00000000-0001-0000-0000-000000000001') # requests.post(url + 'qkd_node/qkd_applications/qkd_app=00000000-0001-0000-0000-000000000001', json=json.loads(enc.dumps(app))) # # Get all apps # apps = node.qkd_applications # z = requests.get(url + 'qkd_node/qkd_applications').json() # var = dec.load_json(z, None, None, obj=apps) # print(enc.dumps(var))