CiscoDevNet / ydk-gen

Generate model-driven APIs from YANG models
http://ciscodevnet.github.io/ydk-gen/
Apache License 2.0
135 stars 74 forks source link

Incorrect bundle model reference #1091

Closed ldacol closed 1 year ago

ldacol commented 1 year ago

Issue tracker is ONLY used for reporting bugs. Please use the YDK Community for any support issues.

Expected Behavior

I should be able to get an XML generated code from the correct bundle family

Current Behavior

Using the incorrect bundle juniper_junos_18_4R3/_yang

Steps to Reproduce

Run below script with the following yaml payload or any payload and see that name spaces in the XML payload are junos instead of junos-qfx

junos-conf-root:configuration:
  version: 20.2R3.9
  junos-conf-chassis:chassis:
      aggregated-devices:
        ethernet:
          device-count: 5
      redundancy:
        graceful-switchover: {}

Your Script

import json
import yaml
import xmltodict
import xml.etree.ElementTree
from lxml import etree
import re
import os
import json

from ydk.providers import CodecServiceProvider
from ydk.services import CodecService

ydk_model_path_juniper_junos_18_4R3 = '/Users/ldacol/ydk_vne/lib/python3.8/site-packages/ydk/models/juniper_junos_18_4R3/_yang'
ydk_model_path_juniper_junos_20_2R3 = '/Users/ldacol/ydk_vne/lib/python3.8/site-packages/ydk/models/juniper_junos_20_2R3/_yang'
ydk_model_path_juniper_junos_20_4R3 = '/Users/ldacol/ydk_vne/lib/python3.8/site-packages/ydk/models/juniper_junos_20_4R3/_yang'
ydk_model_path_juniper_junos_qfx_20_2R3 = '/Users/ldacol/ydk_vne/lib/python3.8/site-packages/ydk/models/juniper_junos_qfx_20_2R3/_yang'

def yang_yaml_to_xml(yaml_file, juniper_family, juniper_version):

    mode = 'rb'

    CODEC = CodecService()

    JSON_PROVIDER = CodecServiceProvider(type='json')
    XML_PROVIDER = CodecServiceProvider(type='xml')

    with open(yaml_file, mode) as yaml_in:
        yaml_object = yaml.safe_load(yaml_in)

    if juniper_family == 'junos':
        if juniper_version == '18.4R3':
            JSON_PROVIDER.initialize('juniper-junos-184R3', ydk_model_path_juniper_junos_18_4R3)
            XML_PROVIDER.initialize('juniper-junos-184R3', ydk_model_path_juniper_junos_18_4R3)
        elif juniper_version == '20.2R3':
            JSON_PROVIDER.initialize('juniper-junos-202R3', ydk_model_path_juniper_junos_20_2R3)
            XML_PROVIDER.initialize('juniper-junos-202R3', ydk_model_path_juniper_junos_20_2R3)
        elif juniper_version == '20.4R3':
            JSON_PROVIDER.initialize('juniper-junos-204R3', ydk_model_path_juniper_junos_20_4R3)
            XML_PROVIDER.initialize('juniper-junos-204R3', ydk_model_path_juniper_junos_20_4R3)
    elif juniper_family == 'junos-qfx':
        if juniper_version == '20.2R3':
            JSON_PROVIDER.initialize('juniper-junos-qfx-202R3', ydk_model_path_juniper_junos_qfx_20_2R3)
            XML_PROVIDER.initialize('juniper-junos-qfx-202R3', ydk_model_path_juniper_junos_qfx_20_2R3)

    config_json_yang = json.dumps(yaml_object)
    decoded_json_yang = CODEC.decode(JSON_PROVIDER, config_json_yang)

    yang_xml = CODEC.encode(XML_PROVIDER, decoded_json_yang)

    return yang_xml

# Function Call
yang_yaml_to_xml(yaml_payload, 'junos-qfx' , '20.2R3')

Logs

Enable logging and post the logs below

YDK conversion from a JSON payload completes, however the incorrect bundle is used.

A debug session of the python code shows that JSON_PROVIDER and XML_PROVIDER reference to the correct root_schema_table before running decoded_json_yang = CODEC.decode(JSON_PROVIDER, config_json_yang) however after CODEC.decode is run the JSON_PROVIDER refers to two root_schema_table (junos-18_4R3 and junos-qfx-202R3). Eventually the decoded_json_yang variable contains elements referring to the incorrect module. Removing the Junos 18.4 modules will lead to an exception "Hit an Exception: No YDK bundle installed for node path 'junos-conf-root:configuration'"

<configuration xmlns="http://yang.juniper.net/junos/conf/root">       < ---------- should be junos-qfx
  <version>20.2R3.9</version>
  <vlans xmlns="http://yang.juniper.net/junos/conf/vlans">          < ---------- should be junos-qfx
    <vlan>
      ........
    </vlan>

System Information

Issue can be reproduced both on a Mac and a Linux system with YDK 0.8.6.5 installed and python 3.8 (Mac) or 3.6 (Linux Centos)

ygorelik commented 1 year ago

Reproducing script:

import os
import logging

from ydk.providers import CodecServiceProvider
from ydk.services import CodecService

import ydk.models.cisco_ios_xr as xr_663
import ydk.models.cisco_iosxr_demo as xr_673

ydk_model_path_iosxr_663 = os.path.join(xr_663.__path__[0], '_yang')
ydk_model_path_iosxr_673 = os.path.join(xr_673.__path__[0], '_yang')

def enable_logging(level):
    log = logging.getLogger('ydk')
    log.setLevel(level)
    handler = logging.StreamHandler()
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    handler.setFormatter(formatter)
    log.addHandler(handler)

def json_to_xml(json_str, xr_version):

    codec = CodecService()

    JSON_PROVIDER = CodecServiceProvider(type='json')
    XML_PROVIDER = CodecServiceProvider(type='xml')

    if xr_version == '663':
        JSON_PROVIDER.initialize('cisco_ios_xr', ydk_model_path_iosxr_663)
        XML_PROVIDER.initialize('cisco_ios_xr', ydk_model_path_iosxr_663)
    elif xr_version == '673':
        JSON_PROVIDER.initialize('cisco_iosxr_demo', ydk_model_path_iosxr_673)
        XML_PROVIDER.initialize('cisco_iosxr_demo', ydk_model_path_iosxr_673)
    else:
        print(f'UNKNOWN XR VERSION {xr_version}')
        return ''

    decoded_json_yang = codec.decode(JSON_PROVIDER, json_str)

    yang_xml = codec.encode(XML_PROVIDER, decoded_json_yang)

    return yang_xml

json_payload = '''
{
  "Cisco-IOS-XR-ifmgr-cfg:interface-configurations": {
    "interface-configuration": [
      {
        "active": "act",
        "interface-name": "Loopback0"
      }
    ]
  }
}
'''

if __name__ == '__main__':
    enable_logging(logging.DEBUG)
    xml = json_to_xml(json_payload, '673')
    print(xml)
ygorelik commented 1 year ago

After the bug fix we can see in the debug level log (attached) that all modules are loaded from requested bundle. Example:

2023-09-07 11:04:28,906 - ydk - DEBUG - Extracting module names from JSON payload
2023-09-07 11:04:28,908 - ydk - DEBUG - Loading module 'Cisco-IOS-XR-ifmgr-cfg', revision ''
2023-09-07 11:04:28,909 - ydk - DEBUG - [libyang] Searching for "Cisco-IOS-XR-ifmgr-cfg" in /Users/ygorelik/venv3.10/lib/python3.10/site-packages/ydk/models/cisco_iosxr_demo/_yang.
2023-09-07 11:04:28,922 - ydk - DEBUG - [libyang] Searching for "Cisco-IOS-XR-ifmgr-cfg" in /Users/ygorelik/ydk-gen/scripts/issues/1091.
2023-09-07 11:04:28,924 - ydk - DEBUG - [libyang] Loading schema from "/Users/ygorelik/venv3.10/lib/python3.10/site-packages/ydk/models/cisco_iosxr_demo/_yang/Cisco-IOS-XR-ifmgr-cfg.yang" file.

ydk.log