Files
oopt-gnpy/gnpy/tools/convert_legacy_yang.py
EstherLerouzic 1a795639c7 feat: Add conversion utilities for YANG and legacy formats in GNPy
This commit introduces new functions for converting between YANG formatted files and
legacy formats. The conversion processes adhere to RFC7951 for encoding YANG data.

Key changes include:
- Conversion of float and empty type representations.
- Transformation of Span and SI lists xx_power_range into dictionaries.
- Addition of necessary namespaces.
- use of oopt-gnpy-libyang to enforce compliancy to yang models

These utilities enable full compatibility with GNPy.

Co-authored-by: Renato Ambrosone <renato.ambrosone@polito.it>

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
Change-Id: Ia004113bca2b0631d1648564e5ccb60504fe80f8
2025-09-03 10:34:14 +02:00

244 lines
10 KiB
Python

#!/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-<file_name>.json or'
+ 'GNPy_yang_formatted-<file_name>.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()