#!/usr/bin/env python3 # -*- coding: utf-8 -*- # SPDX-License-Identifier: BSD-3-Clause # JSON files format conversion legacy <-> YANG # Copyright (C) 2025 Telecom Infra Project and GNPy contributors # see AUTHORS.rst for a list of contributors """ YANG formatted to legacy format conversion ========================================== """ from argparse import ArgumentParser from pathlib import Path from copy import deepcopy import json from typing import Dict from gnpy.tools.yang_convert_utils import convert_degree, convert_back_degree, \ convert_delta_power_range, convert_back_delta_power_range, \ convert_dict, convert_back, \ remove_null_region_city, remove_union_that_fail, \ convert_design_band, convert_back_design_band, \ convert_none_to_empty, convert_empty_to_none, \ convert_loss_coeff_list, convert_back_loss_coeff_list, \ ELEMENTS_KEY, PATH_REQUEST_KEY, RESPONSE_KEY, SPECTRUM_KEY, EQPT_TYPES, EDFA_CONFIG_KEYS, SIM_PARAMS_KEYS, \ TOPO_NMSP, SERV_NMSP, EQPT_NMSP, SPECTRUM_NMSP, SIM_PARAMS_NMSP, EDFA_CONFIG_NMSP, RESP_NMSP, \ dump_data, add_missing_default_type_variety, \ remove_namespace_context, load_data, reorder_route_objects, reorder_lumped_losses_objects, \ reorder_raman_pumps, convert_raman_coef, convert_back_raman_coef, convert_raman_efficiency, \ convert_back_raman_efficiency, convert_nf_coef, convert_back_nf_coef, \ convert_nf_fit_coef, convert_back_nf_fit_coef def legacy_to_yang(json_data: Dict) -> Dict: """Convert legacy format to GNPy YANG format. This function adds the required namespace if not present and processes the input JSON data based on its structure to convert it to the appropriate YANG format. There is no validation of yang formatted data. :param json_data: The input JSON data to convert. :type json_data: Dict :return: The converted JSON data in GNPy YANG format. :rtype: Dict """ json_data = convert_none_to_empty(deepcopy(json_data)) # case of topology json if ELEMENTS_KEY in json_data: json_data = reorder_raman_pumps(json_data) json_data = reorder_lumped_losses_objects(json_data) json_data = remove_null_region_city(json_data) json_data = convert_degree(json_data) json_data = convert_design_band(json_data) json_data = convert_loss_coeff_list(json_data) json_data = convert_raman_coef(json_data) json_data = {TOPO_NMSP: json_data} elif TOPO_NMSP in json_data: # then this is a new format topology json, ensure that there are no issues json_data[TOPO_NMSP] = convert_degree(json_data[TOPO_NMSP]) json_data[TOPO_NMSP] = convert_design_band(json_data[TOPO_NMSP]) json_data[TOPO_NMSP] = convert_loss_coeff_list(json_data[TOPO_NMSP]) json_data[TOPO_NMSP] = remove_null_region_city(json_data[TOPO_NMSP]) # case of equipment json elif any(k in json_data for k in EQPT_TYPES): json_data = convert_raman_efficiency(json_data) json_data = convert_delta_power_range(json_data) json_data = convert_nf_coef(json_data) json_data = add_missing_default_type_variety(json_data) json_data = {EQPT_NMSP: json_data} elif EQPT_NMSP in json_data: # then this is already a new format topology json, ensure that there are no issues json_data[EQPT_NMSP] = convert_raman_efficiency(json_data[EQPT_NMSP]) json_data[EQPT_NMSP] = convert_delta_power_range(json_data[EQPT_NMSP]) json_data[EQPT_NMSP] = convert_nf_coef(json_data[EQPT_NMSP]) json_data[EQPT_NMSP] = add_missing_default_type_variety(json_data[EQPT_NMSP]) # case of service json elif PATH_REQUEST_KEY in json_data: json_data = reorder_route_objects(json_data) json_data = remove_union_that_fail(json_data) json_data = {SERV_NMSP: json_data} elif SERV_NMSP in json_data: json_data[SERV_NMSP] = reorder_route_objects(json_data[SERV_NMSP]) json_data[SERV_NMSP] = remove_union_that_fail(json_data[SERV_NMSP]) # case of edfa_config json elif any(k in json_data for k in EDFA_CONFIG_KEYS): json_data = convert_nf_fit_coef(json_data) json_data = {EDFA_CONFIG_NMSP: json_data} elif EDFA_CONFIG_NMSP in json_data: json_data[EDFA_CONFIG_NMSP] = convert_nf_fit_coef(json_data[EDFA_CONFIG_NMSP]) # case of spectrum json elif SPECTRUM_KEY in json_data: json_data = {SPECTRUM_NMSP: json_data[SPECTRUM_KEY]} # case of sim_params json elif any(k in json_data for k in SIM_PARAMS_KEYS): json_data = {SIM_PARAMS_NMSP: json_data} # case of response json elif RESPONSE_KEY in json_data: json_data = {RESP_NMSP: json_data} elif any(k in json_data for k in [SPECTRUM_NMSP, SIM_PARAMS_NMSP, RESP_NMSP]): # then this is a new format json, nothing to convert pass else: raise ValueError('Unrecognized type of content (not topology, service or equipment)') json_data = convert_dict(json_data) return json_data def yang_to_legacy(json_data: Dict) -> Dict: """Convert GNPy YANG format to legacy format. This function processes the input JSON data to convert it from the new GNPy YANG format back to the legacy format. It handles various types of content, including topology, equipment, and service jsons, ensuring that the necessary conversions are applied. The input data is validated with oopt-gnpy-libyang. :param json_data: The input JSON data in GNPy YANG format to convert. :type json_data: Dict :return: The converted JSON data in legacy format. :rtype: Dict :raises ValueError: If the input JSON data does not match any recognized content type (not topology, service, or equipment). """ # validate data compliance: make sure that this is yang formated data before validation. load_data(json.dumps(legacy_to_yang(json_data))) json_data = convert_empty_to_none(json_data) json_data = convert_back(json_data) # case of topology json if ELEMENTS_KEY in json_data: json_data = convert_back_degree(json_data) json_data = convert_back_design_band(json_data) json_data = convert_back_loss_coeff_list(json_data) json_data = convert_back_raman_coef(json_data) elif TOPO_NMSP in json_data: json_data = convert_back_degree(json_data[TOPO_NMSP]) json_data = convert_back_design_band(json_data) json_data = convert_back_loss_coeff_list(json_data) json_data = convert_back_raman_coef(json_data) # case of equipment json elif any(k in json_data for k in EQPT_TYPES): json_data = convert_back_delta_power_range(json_data) json_data = convert_back_raman_efficiency(json_data) json_data = convert_back_nf_coef(json_data) json_data = remove_namespace_context(json_data, "gnpy-eqpt-config:") elif EQPT_NMSP in json_data: json_data[EQPT_NMSP] = convert_back_delta_power_range(json_data[EQPT_NMSP]) json_data[EQPT_NMSP] = convert_back_raman_efficiency(json_data[EQPT_NMSP]) json_data[EQPT_NMSP] = convert_back_nf_coef(json_data[EQPT_NMSP]) json_data = remove_namespace_context(json_data[EQPT_NMSP], "gnpy-eqpt-config:") # case of EDFA config json elif any(k in json_data for k in EDFA_CONFIG_KEYS): json_data = convert_back_nf_fit_coef(json_data) elif EDFA_CONFIG_NMSP in json_data: json_data[EDFA_CONFIG_NMSP] = convert_back_nf_fit_coef(json_data[EDFA_CONFIG_NMSP]) # case of service json elif SERV_NMSP in json_data: json_data = json_data[SERV_NMSP] # case of sim_params json elif SIM_PARAMS_NMSP in json_data: json_data = json_data[SIM_PARAMS_NMSP] # case of spectrum json elif SPECTRUM_NMSP in json_data: json_data = {SPECTRUM_KEY: json_data[SPECTRUM_NMSP]} # case of planning response json elif RESP_NMSP in json_data: json_data = json_data[RESP_NMSP] elif any(k in json_data for k in SIM_PARAMS_KEYS + [SPECTRUM_KEY, RESPONSE_KEY, PATH_REQUEST_KEY]): # then this is a legacy format json, nothing to convert pass else: raise ValueError('Unrecognized type of content (not topology, service or equipment)') return json_data def main(): """Conversion function """ parser = ArgumentParser() parser.add_argument('--legacy-to-yang', nargs='?', type=Path, help='convert file with this name into yangconformedname.json') parser.add_argument('--yang-to-legacy', nargs='?', type=Path, help='convert file with this name into gnpy' + ' using decimal instead of strings and null instead of [null]') parser.add_argument('--validate', nargs='?', type=Path, help='validate yang conformity') parser.add_argument('-o', '--output', type=Path, help='Stores into file with this name; default = GNPy_legacy_formatted-.json or' + 'GNPy_yang_formatted-.json') args = parser.parse_args() if not (args.legacy_to_yang or args.yang_to_legacy or args.validate): parser.error("You must specify at least one of --legacy-to-yang, --yang-to-legacy, or --validate ") output = None converted = None if args.validate: with open(args.validate, 'r', encoding='utf-8') as f: json_data = json.load(f) load_data(json.dumps(json_data)) return 0 elif args.legacy_to_yang: prefix = 'GNPy_yang_formatted-' with open(args.legacy_to_yang, 'r', encoding='utf-8') as f: json_data = json.load(f) # note that dump_data automatically validate date against yang models converted = dump_data(legacy_to_yang(json_data)) output = prefix + str(args.legacy_to_yang.name) elif args.yang_to_legacy: prefix = 'GNPy_legacy_formatted-' with open(args.yang_to_legacy, 'r', encoding='utf-8') as f: json_data = json.load(f) converted = json.dumps(yang_to_legacy(json_data), indent=2, ensure_ascii=False) output = prefix + str(args.yang_to_legacy.name) if args.output: output = args.output with open(output, 'w', encoding='utf-8') as f: f.write(converted) if __name__ == '__main__': main()