This repository has been archived on 2025-03-20. You can view files and clone it, but cannot push or open issues or pull requests.
sonic-buildimage/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py
Praveen Chaudhary 41af024d14
[sonic-yang-models]: Test to Validate yang models with SONiC config. (#6637)
Changes:
— Remove unnecessary file libyang-python-tests/sample_config_db.json
— Keep only config in format of sonic_yang.json in yang_model_tests/yangTest.json.
— Keep config in format of config_db.json in file tests/files/sample_config_db.json
— Add a test case to validate yang models.


**- Why I did it**
As per discussion in sonic Yang workgroup, arranging files better so that Yang model can be in force for new config, also adding a test case to validate young models upto some extent.

**- How I did it**
Changes:
— Remove unnecessary file libyang-python-tests/sample_config_db.json
— Keep only config in format of sonic_yang.json in yang_model_tests/yangTest.json.
— Keep config in format of config_db.json in file tests/files/sample_config_db.json
— Add a test case to validate yang models.

**- How to verify it**
Build PKGs----Building-----
2021-03-09 18:48:35 -08:00

366 lines
12 KiB
Python

import sys
import os
import pytest
import sonic_yang as sy
import json
import glob
import logging
from ijson import items as ijson_itmes
test_path = os.path.dirname(os.path.abspath(__file__))
modules_path = os.path.dirname(test_path)
sys.path.insert(0, modules_path)
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger("YANG-TEST")
log.setLevel(logging.INFO)
log.addHandler(logging.NullHandler())
class Test_SonicYang(object):
# class vars
@pytest.fixture(autouse=True, scope='class')
def data(self):
test_file = "./tests/libyang-python-tests/test_SonicYang.json"
data = self.jsonTestParser(test_file)
return data
@pytest.fixture(autouse=True, scope='class')
def yang_s(self, data):
yang_dir = str(data['yang_dir'])
yang_s = sy.SonicYang(yang_dir)
return yang_s
def jsonTestParser(self, file):
"""
Open the json test file
"""
with open(file) as data_file:
data = json.load(data_file)
return data
"""
Get the JSON input based on func name
and return jsonInput
"""
def readIjsonInput(self, yang_test_file, test):
try:
# load test specific Dictionary, using Key = func
# this is to avoid loading very large JSON in memory
print(" Read JSON Section: " + test)
jInput = ""
with open(yang_test_file, 'rb') as f:
jInst = ijson_itmes(f, test)
for it in jInst:
jInput = jInput + json.dumps(it)
except Exception as e:
print("Reading Ijson failed")
raise(e)
return jInput
def setup_class(self):
pass
def load_yang_model_file(self, yang_s, yang_dir, yang_file, module_name):
yfile = yang_dir + yang_file
try:
yang_s._load_schema_module(str(yfile))
except Exception as e:
print(e)
raise
#test load and get yang module
def test_load_yang_model_files(self, data, yang_s):
yang_dir = data['yang_dir']
for module in data['modules']:
file = str(module['file'])
module = str(module['module'])
self.load_yang_model_file(yang_s, yang_dir, file, module)
assert yang_s._get_module(module) is not None
#test load non-exist yang module file
def test_load_invalid_model_files(self, data, yang_s):
yang_dir = data['yang_dir']
file = "invalid.yang"
module = "invalid"
with pytest.raises(Exception):
assert self.load_yang_model_file(yang_s, yang_dir, file, module)
#test load yang modules in directory
def test_load_yang_model_dir(self, data, yang_s):
yang_dir = data['yang_dir']
yang_s._load_schema_modules(str(yang_dir))
for module_name in data['modules']:
assert yang_s._get_module(str(module_name['module'])) is not None
#test load yang modules and data files
def test_load_yang_model_data(self, data, yang_s):
yang_dir = str(data['yang_dir'])
yang_files = glob.glob(yang_dir+"/*.yang")
data_file = str(data['data_file'])
data_merge_file = str(data['data_merge_file'])
data_files = []
data_files.append(data_file)
data_files.append(data_merge_file)
print(yang_files)
yang_s._load_data_model(yang_dir, yang_files, data_files)
#validate the data tree from data_merge_file is loaded
for node in data['merged_nodes']:
xpath = str(node['xpath'])
value = str(node['value'])
val = yang_s._find_data_node_value(xpath)
assert str(val) == str(value)
#test load data file
def test_load_data_file(self, data, yang_s):
data_file = str(data['data_file'])
yang_s._load_data_file(data_file)
#test_validate_data_tree():
def test_validate_data_tree(self, data, yang_s):
yang_s.validate_data_tree()
#test find node
def test_find_node(self, data, yang_s):
for node in data['data_nodes']:
expected = node['valid']
xpath = str(node['xpath'])
dnode = yang_s._find_data_node(xpath)
if(expected == "True"):
assert dnode is not None
assert dnode.path() == xpath
else:
assert dnode is None
#test add node
def test_add_node(self, data, yang_s):
for node in data['new_nodes']:
xpath = str(node['xpath'])
value = node['value']
yang_s._add_data_node(xpath, str(value))
data_node = yang_s._find_data_node(xpath)
assert data_node is not None
#test find node value
def test_find_data_node_value(self, data, yang_s):
for node in data['node_values']:
xpath = str(node['xpath'])
value = str(node['value'])
print(xpath)
print(value)
val = yang_s._find_data_node_value(xpath)
assert str(val) == str(value)
#test delete data node
def test_delete_node(self, data, yang_s):
for node in data['delete_nodes']:
xpath = str(node['xpath'])
yang_s._deleteNode(xpath)
#test set node's value
def test_set_datanode_value(self, data, yang_s):
for node in data['set_nodes']:
xpath = str(node['xpath'])
value = node['value']
yang_s._set_data_node_value(xpath, value)
val = yang_s._find_data_node_value(xpath)
assert str(val) == str(value)
#test list of members
def test_find_members(self, yang_s, data):
for node in data['members']:
members = node['members']
xpath = str(node['xpath'])
list = yang_s._find_data_nodes(xpath)
assert list.sort() == members.sort()
#get parent xpath
def test_get_parent_data_xpath(self, yang_s, data):
for node in data['parents']:
xpath = str(node['xpath'])
expected_xpath = str(node['parent'])
path = yang_s._get_parent_data_xpath(xpath)
assert path == expected_xpath
#test find_data_node_schema_xpath
def test_find_data_node_schema_xpath(self, yang_s, data):
for node in data['schema_nodes']:
xpath = str(node['xpath'])
schema_xpath = str(node['value'])
path = yang_s._find_data_node_schema_xpath(xpath)
assert path == schema_xpath
#test data dependencies
def test_find_data_dependencies(self, yang_s, data):
for node in data['dependencies']:
xpath = str(node['xpath'])
list = node['dependencies']
depend = yang_s.find_data_dependencies(xpath)
assert set(depend) == set(list)
#test data dependencies
def test_find_schema_dependencies(self, yang_s, data):
for node in data['schema_dependencies']:
xpath = str(node['xpath'])
list = node['schema_dependencies']
depend = yang_s._find_schema_dependencies(xpath)
assert set(depend) == set(list)
#test merge data tree
def test_merge_data_tree(self, data, yang_s):
data_merge_file = data['data_merge_file']
yang_dir = str(data['yang_dir'])
yang_s._merge_data(data_merge_file, yang_dir)
#yang_s.root.print_mem(ly.LYD_JSON, ly.LYP_FORMAT)
#test get module prefix
def test_get_module_prefix(self, yang_s, data):
for node in data['prefix']:
xpath = str(node['module_name'])
expected = node['module_prefix']
prefix = yang_s._get_module_prefix(xpath)
assert expected == prefix
#test get data type
def test_get_data_type(self, yang_s, data):
for node in data['data_type']:
xpath = str(node['xpath'])
expected = node['data_type']
expected_type = yang_s._str_to_type(expected)
data_type = yang_s._get_data_type(xpath)
assert expected_type == data_type
def test_get_leafref_type(self, yang_s, data):
for node in data['leafref_type']:
xpath = str(node['xpath'])
expected = node['data_type']
expected_type = yang_s._str_to_type(expected)
data_type = yang_s._get_leafref_type(xpath)
assert expected_type == data_type
def test_get_leafref_path(self, yang_s, data):
for node in data['leafref_path']:
xpath = str(node['xpath'])
expected_path = node['leafref_path']
path = yang_s._get_leafref_path(xpath)
assert expected_path == path
def test_get_leafref_type_schema(self, yang_s, data):
for node in data['leafref_type_schema']:
xpath = str(node['xpath'])
expected = node['data_type']
expected_type = yang_s._str_to_type(expected)
data_type = yang_s._get_leafref_type_schema(xpath)
assert expected_type == data_type
"""
This is helper function to load YANG models for tests cases, which works
on Real SONiC Yang models. Mainly tests for translation and reverse
translation.
"""
@pytest.fixture(autouse=True, scope='class')
def sonic_yang_data(self):
sonic_yang_dir = "../sonic-yang-models/yang-models/"
sonic_yang_test_file = "../sonic-yang-models/tests/files/sample_config_db.json"
syc = sy.SonicYang(sonic_yang_dir)
syc.loadYangModel()
sonic_yang_data = dict()
sonic_yang_data['yang_dir'] = sonic_yang_dir
sonic_yang_data['test_file'] = sonic_yang_test_file
sonic_yang_data['syc'] = syc
return sonic_yang_data
def test_validate_yang_models(self, sonic_yang_data):
'''
In this test, we validate yang models
a.) by converting the config as per RFC 7951 using YANG Models,
b.) by creating data tree using new YANG models and
c.) by validating config against YANG models.
Successful execution of these steps can be treated as
validation of new Yang models.
'''
test_file = sonic_yang_data['test_file']
syc = sonic_yang_data['syc']
# Currently only 2 YANG files are not directly related to config
# which are: sonic-extension.yang and sonic-types.yang. Hard coding
# it right now.
# If any more such helper yang files are added, we need to update here.
NON_CONFIG_YANG_FILES = 2
# read config
jIn = self.readIjsonInput(test_file, 'SAMPLE_CONFIG_DB_JSON')
jIn = json.loads(jIn)
numTables = len(jIn)
# load config and create Data tree
syc.loadData(jIn, debug=True)
# check all tables are loaded and config related to all Yang Models is
# loaded in Data tree.
assert len(syc.jIn) == numTables
print("{}:{}".format(len(syc.xlateJson), len(syc.yangFiles)))
assert len(syc.xlateJson) == len(syc.yangFiles) - NON_CONFIG_YANG_FILES
# Validate data tree
validTree = False
try:
syc.validate_data_tree()
validTree = True
except Exception as e:
pass
assert validTree == True
return
def test_xlate_rev_xlate(self, sonic_yang_data):
# In this test, xlation and revXlation is tested with latest Sonic
# YANG model.
test_file = sonic_yang_data['test_file']
syc = sonic_yang_data['syc']
jIn = self.readIjsonInput(test_file, 'SAMPLE_CONFIG_DB_JSON')
jIn = json.loads(jIn)
numTables = len(jIn)
syc.loadData(jIn, debug=True)
# check all tables are loaded and no tables is without Yang Models
assert len(syc.jIn) == numTables
assert len(syc.tablesWithOutYang) == 0
syc.getData(debug=True)
if syc.jIn and syc.jIn == syc.revXlateJson:
print("Xlate and Rev Xlate Passed")
else:
print("Xlate and Rev Xlate failed")
# make it fail
assert False == True
return
def test_table_with_no_yang(self, sonic_yang_data):
# in this test, tables with no YANG models must be stored seperately
# by this library.
test_file = sonic_yang_data['test_file']
syc = sonic_yang_data['syc']
jIn = self.readIjsonInput(test_file, 'SAMPLE_CONFIG_DB_UNKNOWN')
syc.loadData(json.loads(jIn))
ty = syc.tablesWithOutYang
assert (len(ty) and "UNKNOWN_TABLE" in ty)
return
def teardown_class(self):
pass