diff --git a/src/sonic-yang-mgmt/_sonic_yang_ext.py b/src/sonic-yang-mgmt/_sonic_yang_ext.py
index c0e04c217406..8c69f56faf6b 100644
--- a/src/sonic-yang-mgmt/_sonic_yang_ext.py
+++ b/src/sonic-yang-mgmt/_sonic_yang_ext.py
@@ -3,658 +3,656 @@
import yang as ly
import re
-import pprint
import syslog
-from json import dump, load, dumps, loads
+from json import dump, dumps, loads
from xmltodict import parse
-from os import listdir, walk, path
-from os.path import isfile, join, splitext
from glob import glob
-# class sonic_yang methods
-
-"""
-load all YANG models, create JSON of yang models
-"""
-def loadYangModel(self):
-
- try:
- # get all files
- self.yangFiles = glob(self.yang_dir +"/*.yang")
- # load yang modules
- for file in self.yangFiles:
- m = self.load_schema_module(file)
- if m is not None:
- self.sysLog(msg="module: {} is loaded successfully".format(m.name()))
+# class sonic_yang methods, use mixin to extend sonic_yang
+class sonic_yang_ext_mixin:
+
+ """
+ load all YANG models, create JSON of yang models
+ """
+ def loadYangModel(self):
+
+ try:
+ # get all files
+ self.yangFiles = glob(self.yang_dir +"/*.yang")
+ # load yang modules
+ for file in self.yangFiles:
+ m = self.load_schema_module(file)
+ if m is not None:
+ self.sysLog(msg="module: {} is loaded successfully".format(m.name()))
+ else:
+ raise(Exception("Could not load module {}".format(file)))
+
+ # keep only modules name in self.yangFiles
+ self.yangFiles = [f.split('/')[-1] for f in self.yangFiles]
+ self.yangFiles = [f.split('.')[0] for f in self.yangFiles]
+ print('Loaded below Yang Models')
+ print(self.yangFiles)
+
+ # load json for each yang model
+ self.loadJsonYangModel()
+ # create a map from config DB table to yang container
+ self.createDBTableToModuleMap()
+
+ except Exception as e:
+ print("Yang Models Load failed")
+ raise e
+
+ return True
+
+ """
+ load JSON schema format from yang models
+ """
+ def loadJsonYangModel(self):
+
+ try:
+ for f in self.yangFiles:
+ m = self.ctx.get_module(f)
+ if m is not None:
+ xml = m.print_mem(ly.LYD_JSON, ly.LYP_FORMAT)
+ self.yJson.append(parse(xml))
+ self.sysLog(msg="Parsed Json for {}".format(m.name()))
+ except Exception as e:
+ print('JSON conversion for yang models failed')
+ raise e
+
+ return
+
+ """
+ Create a map from config DB tables to container in yang model
+ This module name and topLevelContainer are fetched considering YANG models are
+ written using below Guidelines:
+ https://github.com/Azure/SONiC/blob/master/doc/mgmt/SONiC_YANG_Model_Guidelines.md.
+ """
+ def createDBTableToModuleMap(self):
+
+ for j in self.yJson:
+ # get module name
+ moduleName = j['module']['@name']
+ # topLevelContainer does not exist in sonic-head and sonic-extension.
+ if "sonic-head" in moduleName or "sonic-extension" in moduleName:
+ continue;
+ # get all top level container
+ topLevelContainer = j['module']['container']
+ if topLevelContainer is None:
+ raise Exception("topLevelContainer not found")
+
+ assert topLevelContainer['@name'] == moduleName
+
+ container = topLevelContainer['container']
+ # container is a list
+ if isinstance(container, list):
+ for c in container:
+ self.confDbYangMap[c['@name']] = {
+ "module" : moduleName,
+ "topLevelContainer": topLevelContainer['@name'],
+ "container": c
+ }
+ # container is a dict
else:
- raise(Exception("Could not load module {}".format(file)))
-
- # keep only modules name in self.yangFiles
- self.yangFiles = [f.split('/')[-1] for f in self.yangFiles]
- self.yangFiles = [f.split('.')[0] for f in self.yangFiles]
- print('Loaded below Yang Models')
- print(self.yangFiles)
-
- # load json for each yang model
- self.loadJsonYangModel()
- # create a map from config DB table to yang container
- self.createDBTableToModuleMap()
-
- except Exception as e:
- print("Yang Models Load failed")
- raise e
-
- return True
-
-"""
-load JSON schema format from yang models
-"""
-def loadJsonYangModel(self):
-
- try:
- for f in self.yangFiles:
- m = self.ctx.get_module(f)
- if m is not None:
- xml = m.print_mem(ly.LYD_JSON, ly.LYP_FORMAT)
- self.yJson.append(parse(xml))
- self.sysLog(msg="Parsed Json for {}".format(m.name()))
- except Exception as e:
- print('JSON conversion for yang models failed')
- raise e
-
- return
-
-"""
-Create a map from config DB tables to container in yang model
-This module name and topLevelContainer are fetched considering YANG models are
-written using below Guidelines:
-https://github.com/Azure/SONiC/blob/master/doc/mgmt/SONiC_YANG_Model_Guidelines.md.
-"""
-def createDBTableToModuleMap(self):
-
- for j in self.yJson:
- # get module name
- moduleName = j['module']['@name']
- # topLevelContainer does not exist in sonic-head and sonic-extension.
- if "sonic-head" in moduleName or "sonic-extension" in moduleName:
- continue;
- # get all top level container
- topLevelContainer = j['module']['container']
- if topLevelContainer is None:
- raise Exception("topLevelContainer not found")
-
- assert topLevelContainer['@name'] == moduleName
-
- container = topLevelContainer['container']
- # container is a list
- if isinstance(container, list):
- for c in container:
- self.confDbYangMap[c['@name']] = {
+ self.confDbYangMap[container['@name']] = {
"module" : moduleName,
"topLevelContainer": topLevelContainer['@name'],
- "container": c
+ "container": container
}
- # container is a dict
+ return
+
+ """
+ Get module, topLevelContainer(TLC) and json container for a config DB table
+ """
+ def get_module_TLC_container(self, table):
+ cmap = self.confDbYangMap
+ m = cmap[table]['module']
+ t = cmap[table]['topLevelContainer']
+ c = cmap[table]['container']
+ return m, t, c
+
+ """
+ Crop config as per yang models,
+ This Function crops from config only those TABLEs, for which yang models is
+ provided.
+ """
+ def cropConfigDB(self, croppedFile=None, allowExtraTables=True):
+
+ for table in self.jIn.keys():
+ if table not in self.confDbYangMap:
+ if allowExtraTables:
+ del self.jIn[table]
+ else:
+ raise(Exception("No Yang Model Exist for {}".format(table)))
+
+ if croppedFile:
+ with open(croppedFile, 'w') as f:
+ dump(self.jIn, f, indent=4)
+
+ return
+
+ """
+ Extract keys from table entry in Config DB and return in a dict
+
+ Input:
+ tableKey: Config DB Primary Key, Example tableKey = "Vlan111|2a04:5555:45:6709::1/64"
+ keys: key string from YANG list, i.e. 'vlan_name ip-prefix'.
+ regex: A regex to extract keys from tableKeys, good to have it as accurate as possible.
+
+ Return:
+ KeyDict = {"vlan_name": "Vlan111", "ip-prefix": "2a04:5555:45:6709::1/64"}
+ """
+ def extractKey(self, tableKey, keys, regex):
+
+ keyList = keys.split()
+ # get the value groups
+ value = re.match(regex, tableKey)
+ # create the keyDict
+ i = 1
+ keyDict = dict()
+ for k in keyList:
+ if value.group(i):
+ keyDict[k] = value.group(i)
+ else:
+ raise Exception("Value not found for {} in {}".format(k, tableKey))
+ i = i + 1
+
+ return keyDict
+
+ """
+ Fill the dict based on leaf as a list or dict @model yang model object
+ """
+ def fillLeafDict(self, leafs, leafDict, isleafList=False):
+
+ if leafs is None:
+ return
+
+ # fill default values
+ def fillSteps(leaf):
+ leaf['__isleafList'] = isleafList
+ leafDict[leaf['@name']] = leaf
+ return
+
+ if isinstance(leafs, list):
+ for leaf in leafs:
+ #print("{}:{}".format(leaf['@name'], leaf))
+ fillSteps(leaf)
else:
- self.confDbYangMap[container['@name']] = {
- "module" : moduleName,
- "topLevelContainer": topLevelContainer['@name'],
- "container": container
- }
- return
-
-"""
-Get module, topLevelContainer(TLC) and json container for a config DB table
-"""
-def get_module_TLC_container(self, table):
- cmap = self.confDbYangMap
- m = cmap[table]['module']
- t = cmap[table]['topLevelContainer']
- c = cmap[table]['container']
- return m, t, c
-
-"""
-Crop config as per yang models,
-This Function crops from config only those TABLEs, for which yang models is
-provided.
-"""
-def cropConfigDB(self, croppedFile=None, allowExtraTables=True):
-
- for table in self.jIn.keys():
- if table not in self.confDbYangMap:
- if allowExtraTables:
- del self.jIn[table]
+ #print("{}:{}".format(leaf['@name'], leaf))
+ fillSteps(leafs)
+
+ return
+
+ """
+ create a dict to map each key under primary key with a dict yang model.
+ This is done to improve performance of mapping from values of TABLEs in
+ config DB to leaf in YANG LIST.
+ """
+ def createLeafDict(self, model):
+
+ leafDict = dict()
+ #Iterate over leaf, choices and leaf-list.
+ self.fillLeafDict(model.get('leaf'), leafDict)
+
+ #choices, this is tricky, since leafs are under cases in tree.
+ choices = model.get('choice')
+ if choices:
+ for choice in choices:
+ cases = choice['case']
+ for case in cases:
+ self.fillLeafDict(case.get('leaf'), leafDict)
+
+ # leaf-lists
+ self.fillLeafDict(model.get('leaf-list'), leafDict, True)
+
+ return leafDict
+
+ """
+ Convert a string from Config DB value to Yang Value based on type of the
+ key in Yang model.
+ @model : A List of Leafs in Yang model list
+ """
+ def findYangTypedValue(self, key, value, leafDict):
+
+ # convert config DB string to yang Type
+ def yangConvert(val):
+ # Convert everything to string
+ val = str(val)
+ # find type of this key from yang leaf
+ type = leafDict[key]['type']['@name']
+
+ if 'uint' in type:
+ vValue = int(val, 10)
+ # TODO: find type of leafref from schema node
+ elif 'leafref' in type:
+ vValue = val
+ #TODO: find type in sonic-head, as of now, all are enumeration
+ elif 'head:' in type:
+ vValue = val
else:
- raise(Exception("No Yang Model Exist for {}".format(table)))
-
- if croppedFile:
- with open(croppedFile, 'w') as f:
- dump(self.jIn, f, indent=4)
-
- return
-
-"""
-Extract keys from table entry in Config DB and return in a dict
-
-Input:
-tableKey: Config DB Primary Key, Example tableKey = "Vlan111|2a04:5555:45:6709::1/64"
-keys: key string from YANG list, i.e. 'vlan_name ip-prefix'.
-regex: A regex to extract keys from tableKeys, good to have it as accurate as possible.
-
-Return:
-KeyDict = {"vlan_name": "Vlan111", "ip-prefix": "2a04:5555:45:6709::1/64"}
-"""
-def extractKey(self, tableKey, keys, regex):
-
- keyList = keys.split()
- # get the value groups
- value = re.match(regex, tableKey)
- # create the keyDict
- i = 1
- keyDict = dict()
- for k in keyList:
- if value.group(i):
- keyDict[k] = value.group(i)
+ vValue = val
+ return vValue
+
+ # if it is a leaf-list do it for each element
+ if leafDict[key]['__isleafList']:
+ vValue = list()
+ for v in value:
+ vValue.append(yangConvert(v))
else:
- raise Exception("Value not found for {} in {}".format(k, tableKey))
- i = i + 1
+ vValue = yangConvert(value)
- return keyDict
+ return vValue
-"""
-Fill the dict based on leaf as a list or dict @model yang model object
-"""
-def fillLeafDict(self, leafs, leafDict, isleafList=False):
+ """
+ Xlate a list
+ This function will xlate from a dict in config DB to a Yang JSON list
+ using yang model. Output will be go in self.xlateJson
+ """
+ def xlateList(self, model, yang, config, table):
+
+ #create a dict to map each key under primary key with a dict yang model.
+ #This is done to improve performance of mapping from values of TABLEs in
+ #config DB to leaf in YANG LIST.
+ leafDict = self.createLeafDict(model)
+
+ # fetch regex from YANG models.
+ keyRegEx = model['ext:key-regex-configdb-to-yang']['@value']
+ # seperator `|` has special meaning in regex, so change it appropriately.
+ keyRegEx = re.sub('\|', '\\|', keyRegEx)
+ # get keys from YANG model list itself
+ listKeys = model['key']['@value']
+ self.sysLog(msg="xlateList regex:{} keyList:{}".\
+ format(keyRegEx, listKeys))
+
+ for pkey in config.keys():
+ try:
+ vKey = None
+ self.sysLog(syslog.LOG_DEBUG, "xlateList Extract pkey:{}".\
+ format(pkey))
+ # Find and extracts key from each dict in config
+ keyDict = self.extractKey(pkey, listKeys, keyRegEx)
+ # fill rest of the values in keyDict
+ for vKey in config[pkey]:
+ self.sysLog(syslog.LOG_DEBUG, "xlateList vkey {}".format(vKey))
+ keyDict[vKey] = self.findYangTypedValue(vKey, \
+ config[pkey][vKey], leafDict)
+ yang.append(keyDict)
+ # delete pkey from config, done to match one key with one list
+ del config[pkey]
+
+ except Exception as e:
+ # log debug, because this exception may occur with multilists
+ self.sysLog(syslog.LOG_DEBUG, "xlateList Exception {}".format(e))
+ # with multilist, we continue matching other keys.
+ continue
- if leafs == None:
return
- # fill default values
- def fillSteps(leaf):
- leaf['__isleafList'] = isleafList
- leafDict[leaf['@name']] = leaf
+ """
+ Xlate a container
+ This function will xlate from a dict in config DB to a Yang JSON container
+ using yang model. Output will be stored in self.xlateJson
+ """
+ def xlateContainer(self, model, yang, config, table):
+
+ # To Handle multiple List, Make a copy of config, because we delete keys
+ # from config after each match. This is done to match one pkey with one list.
+ configC = config.copy()
+
+ clist = model.get('list')
+ # If single list exists in container,
+ if clist and isinstance(clist, dict) and \
+ clist['@name'] == model['@name']+"_LIST" and bool(configC):
+ #print(clist['@name'])
+ yang[clist['@name']] = list()
+ self.sysLog(msg="xlateContainer listD {}".format(clist['@name']))
+ self.xlateList(clist, yang[clist['@name']], \
+ configC, table)
+ # clean empty lists
+ if len(yang[clist['@name']]) == 0:
+ del yang[clist['@name']]
+ #print(yang[clist['@name']])
+
+ # If multi-list exists in container,
+ elif clist and isinstance(clist, list) and bool(configC):
+ for modelList in clist:
+ yang[modelList['@name']] = list()
+ self.sysLog(msg="xlateContainer listL {}".format(modelList['@name']))
+ self.xlateList(modelList, yang[modelList['@name']], configC, table)
+ # clean empty lists
+ if len(yang[modelList['@name']]) == 0:
+ del yang[modelList['@name']]
+
+ if len(configC):
+ self.sysLog(syslog.LOG_ERR, "Alert: Remaining keys in Config")
+ raise(Exception("All Keys are not parsed in {}".format(table)))
+
return
- if isinstance(leafs, list):
- for leaf in leafs:
- #print("{}:{}".format(leaf['@name'], leaf))
- fillSteps(leaf)
- else:
- #print("{}:{}".format(leaf['@name'], leaf))
- fillSteps(leafs)
-
- return
-
-"""
-create a dict to map each key under primary key with a dict yang model.
-This is done to improve performance of mapping from values of TABLEs in
-config DB to leaf in YANG LIST.
-"""
-def createLeafDict(self, model):
-
- leafDict = dict()
- #Iterate over leaf, choices and leaf-list.
- self.fillLeafDict(model.get('leaf'), leafDict)
-
- #choices, this is tricky, since leafs are under cases in tree.
- choices = model.get('choice')
- if choices:
- for choice in choices:
- cases = choice['case']
- for case in cases:
- self.fillLeafDict(case.get('leaf'), leafDict)
-
- # leaf-lists
- self.fillLeafDict(model.get('leaf-list'), leafDict, True)
-
- return leafDict
-
-"""
-Convert a string from Config DB value to Yang Value based on type of the
-key in Yang model.
-@model : A List of Leafs in Yang model list
-"""
-def findYangTypedValue(self, key, value, leafDict):
-
- # convert config DB string to yang Type
- def yangConvert(val):
- # Convert everything to string
- val = str(val)
- # find type of this key from yang leaf
- type = leafDict[key]['type']['@name']
-
- if 'uint' in type:
- vValue = int(val, 10)
- # TODO: find type of leafref from schema node
- elif 'leafref' in type:
- vValue = val
- #TODO: find type in sonic-head, as of now, all are enumeration
- elif 'head:' in type:
- vValue = val
+ """
+ xlate ConfigDB json to Yang json
+ """
+ def xlateConfigDBtoYang(self, jIn, yangJ):
+
+ # find top level container for each table, and run the xlate_container.
+ for table in jIn.keys():
+ cmap = self.confDbYangMap[table]
+ # create top level containers
+ key = cmap['module']+":"+cmap['topLevelContainer']
+ subkey = cmap['topLevelContainer']+":"+cmap['container']['@name']
+ # Add new top level container for first table in this container
+ yangJ[key] = dict() if yangJ.get(key) is None else yangJ[key]
+ yangJ[key][subkey] = dict()
+ self.sysLog(msg="xlateConfigDBtoYang {}:{}".format(key, subkey))
+ self.xlateContainer(cmap['container'], yangJ[key][subkey], \
+ jIn[table], table)
+
+ return
+
+ """
+ Read config file and crop it as per yang models
+ """
+ def xlateConfigDB(self, xlateFile=None):
+
+ jIn= self.jIn
+ yangJ = self.xlateJson
+ # xlation is written in self.xlateJson
+ self.xlateConfigDBtoYang(jIn, yangJ)
+
+ if xlateFile:
+ with open(xlateFile, 'w') as f:
+ dump(self.xlateJson, f, indent=4)
+
+ return
+
+ """
+ create config DB table key from entry in yang JSON
+ """
+ def createKey(self, entry, regex):
+
+ keyDict = dict()
+ keyV = regex
+ # get the keys from regex of key extractor
+ keyList = re.findall(r'<(.*?)>', regex)
+ for key in keyList:
+ val = entry.get(key)
+ if val:
+ #print("pair: {} {}".format(key, val))
+ keyDict[key] = sval = str(val)
+ keyV = re.sub(r'<'+key+'>', sval, keyV)
+ #print("VAL: {} {}".format(regex, keyV))
+ else:
+ raise Exception("key {} not found in entry".format(key))
+ #print("kDict {}".format(keyDict))
+ return keyV, keyDict
+
+ """
+ Convert a string from Config DB value to Yang Value based on type of the
+ key in Yang model.
+ @model : A List of Leafs in Yang model list
+ """
+ def revFindYangTypedValue(self, key, value, leafDict):
+
+ # convert yang Type to config DB string
+ def revYangConvert(val):
+ # config DB has only strings, thank god for that :), wait not yet!!!
+ return str(val)
+
+ # if it is a leaf-list do it for each element
+ if leafDict[key]['__isleafList']:
+ vValue = list()
+ for v in value:
+ vValue.append(revYangConvert(v))
else:
- vValue = val
+ vValue = revYangConvert(value)
+
return vValue
- # if it is a leaf-list do it for each element
- if leafDict[key]['__isleafList']:
- vValue = list()
- for v in value:
- vValue.append(yangConvert(v))
- else:
- vValue = yangConvert(value)
-
- return vValue
-
-"""
-Xlate a list
-This function will xlate from a dict in config DB to a Yang JSON list
-using yang model. Output will be go in self.xlateJson
-"""
-def xlateList(self, model, yang, config, table):
-
- #create a dict to map each key under primary key with a dict yang model.
- #This is done to improve performance of mapping from values of TABLEs in
- #config DB to leaf in YANG LIST.
- leafDict = self.createLeafDict(model)
-
- # fetch regex from YANG models.
- keyRegEx = model['ext:key-regex-configdb-to-yang']['@value']
- # seperator `|` has special meaning in regex, so change it appropriately.
- keyRegEx = re.sub('\|', '\\|', keyRegEx)
- # get keys from YANG model list itself
- listKeys = model['key']['@value']
- self.sysLog(msg="xlateList regex:{} keyList:{}".\
- format(keyRegEx, listKeys))
-
- for pkey in config.keys():
- try:
- vKey = None
- self.sysLog(syslog.LOG_DEBUG, "xlateList Extract pkey:{}".\
- format(pkey))
- # Find and extracts key from each dict in config
- keyDict = self.extractKey(pkey, listKeys, keyRegEx)
- # fill rest of the values in keyDict
- for vKey in config[pkey]:
- self.sysLog(syslog.LOG_DEBUG, "xlateList vkey {}".format(vKey))
- keyDict[vKey] = self.findYangTypedValue(vKey, \
- config[pkey][vKey], leafDict)
- yang.append(keyDict)
- # delete pkey from config, done to match one key with one list
- del config[pkey]
+ """
+ Rev xlate from
_LIST to table in config DB
+ """
+ def revXlateList(self, model, yang, config, table):
+
+ # fetch regex from YANG models
+ keyRegEx = model['ext:key-regex-yang-to-configdb']['@value']
+ self.sysLog(msg="revXlateList regex:{}".format(keyRegEx))
+
+ # create a dict to map each key under primary key with a dict yang model.
+ # This is done to improve performance of mapping from values of TABLEs in
+ # config DB to leaf in YANG LIST.
+ leafDict = self.createLeafDict(model)
+
+ # list with name _LIST should be removed,
+ if "_LIST" in model['@name']:
+ for entry in yang:
+ # create key of config DB table
+ pkey, pkeydict = self.createKey(entry, keyRegEx)
+ self.sysLog(syslog.LOG_DEBUG, "revXlateList pkey:{}".format(pkey))
+ config[pkey]= dict()
+ # fill rest of the entries
+ for key in entry:
+ if key not in pkeydict:
+ config[pkey][key] = self.revFindYangTypedValue(key, \
+ entry[key], leafDict)
- except Exception as e:
- # log debug, because this exception may occur with multilists
- self.sysLog(syslog.LOG_DEBUG, "xlateList Exception {}".format(e))
- # with multilist, we continue matching other keys.
- continue
-
- return
-
-"""
-Xlate a container
-This function will xlate from a dict in config DB to a Yang JSON container
-using yang model. Output will be stored in self.xlateJson
-"""
-def xlateContainer(self, model, yang, config, table):
-
- # To Handle multiple List, Make a copy of config, because we delete keys
- # from config after each match. This is done to match one pkey with one list.
- configC = config.copy()
-
- clist = model.get('list')
- # If single list exists in container,
- if clist and isinstance(clist, dict) and \
- clist['@name'] == model['@name']+"_LIST" and bool(configC):
- #print(clist['@name'])
- yang[clist['@name']] = list()
- self.sysLog(msg="xlateContainer listD {}".format(clist['@name']))
- self.xlateList(clist, yang[clist['@name']], \
- configC, table)
- # clean empty lists
- if len(yang[clist['@name']]) == 0:
- del yang[clist['@name']]
- #print(yang[clist['@name']])
-
- # If multi-list exists in container,
- elif clist and isinstance(clist, list) and bool(configC):
- for modelList in clist:
- yang[modelList['@name']] = list()
- self.sysLog(msg="xlateContainer listL {}".format(modelList['@name']))
- self.xlateList(modelList, yang[modelList['@name']], configC, table)
- # clean empty lists
- if len(yang[modelList['@name']]) == 0:
- del yang[modelList['@name']]
-
- if len(configC):
- self.sysLog(syslog.LOG_ERR, "Alert: Remaining keys in Config")
- raise(Exception("All Keys are not parsed in {}".format(table)))
-
- return
-
-"""
-xlate ConfigDB json to Yang json
-"""
-def xlateConfigDBtoYang(self, jIn, yangJ):
-
- # find top level container for each table, and run the xlate_container.
- for table in jIn.keys():
- cmap = self.confDbYangMap[table]
- # create top level containers
- key = cmap['module']+":"+cmap['topLevelContainer']
- subkey = cmap['topLevelContainer']+":"+cmap['container']['@name']
- # Add new top level container for first table in this container
- yangJ[key] = dict() if yangJ.get(key) is None else yangJ[key]
- yangJ[key][subkey] = dict()
- self.sysLog(msg="xlateConfigDBtoYang {}:{}".format(key, subkey))
- self.xlateContainer(cmap['container'], yangJ[key][subkey], \
- jIn[table], table)
-
- return
-
-"""
-Read config file and crop it as per yang models
-"""
-def xlateConfigDB(self, xlateFile=None):
-
- jIn= self.jIn
- yangJ = self.xlateJson
- # xlation is written in self.xlateJson
- self.xlateConfigDBtoYang(jIn, yangJ)
-
- if xlateFile:
- with open(xlateFile, 'w') as f:
- dump(self.xlateJson, f, indent=4)
-
- return
-
-"""
-create config DB table key from entry in yang JSON
-"""
-def createKey(self, entry, regex):
-
- keyDict = dict()
- keyV = regex
- # get the keys from regex of key extractor
- keyList = re.findall(r'<(.*?)>', regex)
- for key in keyList:
- val = entry.get(key)
- if val:
- #print("pair: {} {}".format(key, val))
- keyDict[key] = sval = str(val)
- keyV = re.sub(r'<'+key+'>', sval, keyV)
- #print("VAL: {} {}".format(regex, keyV))
- else:
- raise Exception("key {} not found in entry".format(key))
- #print("kDict {}".format(keyDict))
- return keyV, keyDict
-
-"""
-Convert a string from Config DB value to Yang Value based on type of the
-key in Yang model.
-@model : A List of Leafs in Yang model list
-"""
-def revFindYangTypedValue(self, key, value, leafDict):
-
- # convert yang Type to config DB string
- def revYangConvert(val):
- # config DB has only strings, thank god for that :), wait not yet!!!
- return str(val)
-
- # if it is a leaf-list do it for each element
- if leafDict[key]['__isleafList']:
- vValue = list()
- for v in value:
- vValue.append(revYangConvert(v))
- else:
- vValue = revYangConvert(value)
-
- return vValue
-
-"""
-Rev xlate from _LIST to table in config DB
-"""
-def revXlateList(self, model, yang, config, table):
-
- # fetch regex from YANG models
- keyRegEx = model['ext:key-regex-yang-to-configdb']['@value']
- self.sysLog(msg="revXlateList regex:{}".format(keyRegEx))
-
- # create a dict to map each key under primary key with a dict yang model.
- # This is done to improve performance of mapping from values of TABLEs in
- # config DB to leaf in YANG LIST.
- leafDict = self.createLeafDict(model)
-
- # list with name _LIST should be removed,
- if "_LIST" in model['@name']:
- for entry in yang:
- # create key of config DB table
- pkey, pkeydict = self.createKey(entry, keyRegEx)
- self.sysLog(syslog.LOG_DEBUG, "revXlateList pkey:{}".format(pkey))
- config[pkey]= dict()
- # fill rest of the entries
- for key in entry:
- if key not in pkeydict:
- config[pkey][key] = self.revFindYangTypedValue(key, \
- entry[key], leafDict)
-
- return
-
-"""
-Rev xlate from yang container to table in config DB
-"""
-def revXlateContainer(self, model, yang, config, table):
-
- # Note: right now containers has only LISTs.
- # IF container has only one list
- if isinstance(model['list'], dict):
- modelList = model['list']
- # Pass matching list from Yang Json
- self.sysLog(msg="revXlateContainer {}".format(modelList['@name']))
- self.revXlateList(modelList, yang[modelList['@name']], config, table)
-
- elif isinstance(model['list'], list):
- for modelList in model['list']:
+ return
+
+ """
+ Rev xlate from yang container to table in config DB
+ """
+ def revXlateContainer(self, model, yang, config, table):
+
+ # Note: right now containers has only LISTs.
+ # IF container has only one list
+ if isinstance(model['list'], dict):
+ modelList = model['list']
+ # Pass matching list from Yang Json
self.sysLog(msg="revXlateContainer {}".format(modelList['@name']))
self.revXlateList(modelList, yang[modelList['@name']], config, table)
- return
+ elif isinstance(model['list'], list):
+ for modelList in model['list']:
+ self.sysLog(msg="revXlateContainer {}".format(modelList['@name']))
+ self.revXlateList(modelList, yang[modelList['@name']], config, table)
-"""
-rev xlate ConfigDB json to Yang json
-"""
-def revXlateYangtoConfigDB(self, yangJ, cDbJson):
+ return
- yangJ = self.xlateJson
- cDbJson = self.revXlateJson
+ """
+ rev xlate ConfigDB json to Yang json
+ """
+ def revXlateYangtoConfigDB(self, yangJ, cDbJson):
+
+ yangJ = self.xlateJson
+ cDbJson = self.revXlateJson
+
+ # find table in config DB, use name as a KEY
+ for module_top in yangJ.keys():
+ # module _top will be of from module:top
+ for container in yangJ[module_top].keys():
+ #table = container.split(':')[1]
+ table = container
+ #print("revXlate " + table)
+ cmap = self.confDbYangMap[table]
+ cDbJson[table] = dict()
+ #print(key + "--" + subkey)
+ self.sysLog(msg="revXlateYangtoConfigDB {}".format(table))
+ self.revXlateContainer(cmap['container'], yangJ[module_top][container], \
+ cDbJson[table], table)
- # find table in config DB, use name as a KEY
- for module_top in yangJ.keys():
- # module _top will be of from module:top
- for container in yangJ[module_top].keys():
- #table = container.split(':')[1]
- table = container
- #print("revXlate " + table)
- cmap = self.confDbYangMap[table]
- cDbJson[table] = dict()
- #print(key + "--" + subkey)
- self.sysLog(msg="revXlateYangtoConfigDB {}".format(table))
- self.revXlateContainer(cmap['container'], yangJ[module_top][container], \
- cDbJson[table], table)
-
- return
-
-"""
-Reverse Translate tp config DB
-"""
-def revXlateConfigDB(self, revXlateFile=None):
-
- yangJ = self.xlateJson
- cDbJson = self.revXlateJson
- # xlation is written in self.xlateJson
- self.revXlateYangtoConfigDB(yangJ, cDbJson)
-
- if revXlateFile:
- with open(revXlateFile, 'w') as f:
- dump(self.revXlateJson, f, indent=4)
-
- return
-
-"""
-Find a list in YANG Container
-c = container
-l = list name
-return: list if found else None
-"""
-def findYangList(self, container, listName):
-
- if isinstance(container['list'], dict):
- clist = container['list']
- if clist['@name'] == listName:
- return clist
-
- elif isinstance(container['list'], list):
- clist = [l for l in container['list'] if l['@name'] == listName]
- return clist[0]
-
- return None
-
-"""
-Find xpath of the PORT Leaf in PORT container/list. Xpath of Leaf is needed,
-because only leaf can have leafrefs depend on them.
-"""
-def findXpathPortLeaf(self, portName):
-
- try:
- table = "PORT"
- xpath = self.findXpathPort(portName)
- module, topc, container = self.get_module_TLC_container(table)
- list = self.findYangList(container, table+"_LIST")
- xpath = xpath + "/" + list['key']['@value'].split()[0]
- except Exception as e:
- print("find xpath of port Leaf failed")
- raise e
-
- return xpath
-
-
-"""
-Find xpath of PORT
-"""
-def findXpathPort(self, portName):
-
- try:
- table = "PORT"
- module, topc, container = self.get_module_TLC_container(table)
- xpath = "/" + module + ":" + topc + "/" + table
-
- list = self.findYangList(container, table+"_LIST")
- xpath = self.findXpathList(xpath, list, [portName])
- except Exception as e:
- print("find xpath of port failed")
- raise e
-
- return xpath
-
-"""
-Find xpath of a YANG LIST from keys,
-xpath: xpath till list
-list: YANG List
-keys: list of keys in YANG LIST
-"""
-def findXpathList(self, xpath, list, keys):
-
- try:
- # add list name in xpath
- xpath = xpath + "/" + list['@name']
- listKeys = list['key']['@value'].split()
- i = 0;
- for listKey in listKeys:
- xpath = xpath + '['+listKey+'=\''+keys[i]+'\']'
- i = i + 1
- except Exception as e:
- print("find xpath of list failed")
- raise e
-
- return xpath
-
-"""
-load_data: load Config DB, crop, xlate and create data tree from it.
-input: data
-returns: True - success False - failed
-"""
-def load_data(self, configdbJson, allowExtraTables=True):
-
- try:
- self.jIn = configdbJson
- # reset xlate
- self.xlateJson = dict()
- # self.jIn will be cropped
- self.cropConfigDB(allowExtraTables=allowExtraTables)
- # xlated result will be in self.xlateJson
- self.xlateConfigDB()
- #print(self.xlateJson)
- self.sysLog(msg="Try to load Data in the tree")
- self.root = self.ctx.parse_data_mem(dumps(self.xlateJson), \
- ly.LYD_JSON, ly.LYD_OPT_CONFIG|ly.LYD_OPT_STRICT)
-
- except Exception as e:
- self.root = None
- print("Data Loading Failed")
- raise e
-
- return True
-
-"""
-Get data from Data tree, data tree will be assigned in self.xlateJson
-"""
-def get_data(self):
-
- try:
- self.xlateJson = loads(self.print_data_mem('JSON'))
- # reset reverse xlate
- self.revXlateJson = dict()
- # result will be stored self.revXlateJson
- self.revXlateConfigDB()
-
- except Exception as e:
- print("Get Data Tree Failed")
- raise e
-
- return self.revXlateJson
-
-"""
-Delete a node from data tree, if this is LEAF and KEY Delete the Parent
-"""
-def delete_node(self, xpath):
-
- # These MACROS used only here, can we get it from Libyang Header ?
- try:
- LYS_LEAF = 4
- node = self.find_data_node(xpath)
- if node is None:
- raise('Node {} not found'.format(xpath))
-
- snode = node.schema()
- # check for a leaf if it is a key. If yes delete the parent
- if (snode.nodetype() == LYS_LEAF):
- leaf = ly.Schema_Node_Leaf(snode)
- if leaf.is_key():
- # try to delete parent
- nodeP = self.find_parent_data_node(xpath)
- xpathP = nodeP.path()
- if self._delete_node(xpath=xpathP, node=nodeP) == False:
- raise('_delete_node failed')
- else:
- return True
+ return
+
+ """
+ Reverse Translate tp config DB
+ """
+ def revXlateConfigDB(self, revXlateFile=None):
+
+ yangJ = self.xlateJson
+ cDbJson = self.revXlateJson
+ # xlation is written in self.xlateJson
+ self.revXlateYangtoConfigDB(yangJ, cDbJson)
+
+ if revXlateFile:
+ with open(revXlateFile, 'w') as f:
+ dump(self.revXlateJson, f, indent=4)
+
+ return
+
+ """
+ Find a list in YANG Container
+ c = container
+ l = list name
+ return: list if found else None
+ """
+ def findYangList(self, container, listName):
+
+ if isinstance(container['list'], dict):
+ clist = container['list']
+ if clist['@name'] == listName:
+ return clist
+
+ elif isinstance(container['list'], list):
+ clist = [l for l in container['list'] if l['@name'] == listName]
+ return clist[0]
+
+ return None
+
+ """
+ Find xpath of the PORT Leaf in PORT container/list. Xpath of Leaf is needed,
+ because only leaf can have leafrefs depend on them.
+ """
+ def findXpathPortLeaf(self, portName):
- # delete non key element
- if self._delete_node(xpath=xpath, node=node) == False:
- raise('_delete_node failed')
- except Exception as e:
- print(e)
- raise('Failed to delete node {}'.format(xpath))
+ try:
+ table = "PORT"
+ xpath = self.findXpathPort(portName)
+ module, topc, container = self.get_module_TLC_container(table)
+ list = self.findYangList(container, table+"_LIST")
+ xpath = xpath + "/" + list['key']['@value'].split()[0]
+ except Exception as e:
+ print("find xpath of port Leaf failed")
+ raise e
+
+ return xpath
+
+
+ """
+ Find xpath of PORT
+ """
+ def findXpathPort(self, portName):
+
+ try:
+ table = "PORT"
+ module, topc, container = self.get_module_TLC_container(table)
+ xpath = "/" + module + ":" + topc + "/" + table
+
+ list = self.findYangList(container, table+"_LIST")
+ xpath = self.findXpathList(xpath, list, [portName])
+ except Exception as e:
+ print("find xpath of port failed")
+ raise e
+
+ return xpath
+
+ """
+ Find xpath of a YANG LIST from keys,
+ xpath: xpath till list
+ list: YANG List
+ keys: list of keys in YANG LIST
+ """
+ def findXpathList(self, xpath, list, keys):
+
+ try:
+ # add list name in xpath
+ xpath = xpath + "/" + list['@name']
+ listKeys = list['key']['@value'].split()
+ i = 0;
+ for listKey in listKeys:
+ xpath = xpath + '['+listKey+'=\''+keys[i]+'\']'
+ i = i + 1
+ except Exception as e:
+ print("find xpath of list failed")
+ raise e
+
+ return xpath
+
+ """
+ load_data: load Config DB, crop, xlate and create data tree from it.
+ input: data
+ returns: True - success False - failed
+ """
+ def load_data(self, configdbJson, allowExtraTables=True):
+
+ try:
+ self.jIn = configdbJson
+ # reset xlate
+ self.xlateJson = dict()
+ # self.jIn will be cropped
+ self.cropConfigDB(allowExtraTables=allowExtraTables)
+ # xlated result will be in self.xlateJson
+ self.xlateConfigDB()
+ #print(self.xlateJson)
+ self.sysLog(msg="Try to load Data in the tree")
+ self.root = self.ctx.parse_data_mem(dumps(self.xlateJson), \
+ ly.LYD_JSON, ly.LYD_OPT_CONFIG|ly.LYD_OPT_STRICT)
+
+ except Exception as e:
+ self.root = None
+ print("Data Loading Failed")
+ raise e
+
+ return True
+
+ """
+ Get data from Data tree, data tree will be assigned in self.xlateJson
+ """
+ def get_data(self):
+
+ try:
+ self.xlateJson = loads(self.print_data_mem('JSON'))
+ # reset reverse xlate
+ self.revXlateJson = dict()
+ # result will be stored self.revXlateJson
+ self.revXlateConfigDB()
+
+ except Exception as e:
+ print("Get Data Tree Failed")
+ raise e
+
+ return self.revXlateJson
+
+ """
+ Delete a node from data tree, if this is LEAF and KEY Delete the Parent
+ """
+ def delete_node(self, xpath):
+
+ # These MACROS used only here, can we get it from Libyang Header ?
+ try:
+ LYS_LEAF = 4
+ node = self.find_data_node(xpath)
+ if node is None:
+ raise('Node {} not found'.format(xpath))
+
+ snode = node.schema()
+ # check for a leaf if it is a key. If yes delete the parent
+ if (snode.nodetype() == LYS_LEAF):
+ leaf = ly.Schema_Node_Leaf(snode)
+ if leaf.is_key():
+ # try to delete parent
+ nodeP = self.find_parent_node(xpath)
+ xpathP = nodeP.path()
+ if self._delete_node(xpath=xpathP, node=nodeP) == False:
+ raise Exception('_delete_node failed')
+ else:
+ return True
+
+ # delete non key element
+ if self._delete_node(xpath=xpath, node=node) == False:
+ raise Exception('_delete_node failed')
+ except Exception as e:
+ print(e)
+ raise Exception('Failed to delete node {}'.format(xpath))
- return True
+ return True
-# End of class sonic_yang
+ # End of class sonic_yang
diff --git a/src/sonic-yang-mgmt/setup.py b/src/sonic-yang-mgmt/setup.py
index 24a09cf958c7..15c8243aad7f 100644
--- a/src/sonic-yang-mgmt/setup.py
+++ b/src/sonic-yang-mgmt/setup.py
@@ -5,22 +5,21 @@
from setuptools import setup, find_packages
from setuptools.command.build_py import build_py
-from os import system
+from os import system, environ
from sys import exit
import pytest
-import os
# find path of pkgs from os environment vars
-prefix = '/sonic'; debs = os.environ["STRETCH_DEBS_PATH"]
-wheels = os.environ["PYTHON_WHEELS_PATH"]
+prefix = '/sonic'; debs = environ["STRETCH_DEBS_PATH"]
+wheels = environ["PYTHON_WHEELS_PATH"]
wheels_path = '{}/{}'.format(prefix, wheels)
deps_path = '{}/{}'.format(prefix, debs)
# dependencies
-libyang = '{}/{}'.format(deps_path, os.environ["LIBYANG"])
-libyangCpp = '{}/{}'.format(deps_path, os.environ["LIBYANG_CPP"])
-libyangPy2 = '{}/{}'.format(deps_path, os.environ["LIBYANG_PY2"])
-libyangPy3 = '{}/{}'.format(deps_path, os.environ["LIBYANG_PY3"])
-sonicYangModels = '{}/{}'.format(wheels_path, os.environ["SONIC_YANG_MODELS_PY3"])
+libyang = '{}/{}'.format(deps_path, environ["LIBYANG"])
+libyangCpp = '{}/{}'.format(deps_path, environ["LIBYANG_CPP"])
+libyangPy2 = '{}/{}'.format(deps_path, environ["LIBYANG_PY2"])
+libyangPy3 = '{}/{}'.format(deps_path, environ["LIBYANG_PY3"])
+sonicYangModels = '{}/{}'.format(wheels_path, environ["SONIC_YANG_MODELS_PY3"])
# important reuirements parameters
build_requirements = [libyang, libyangCpp, libyangPy2, libyangPy3, sonicYangModels,]
diff --git a/src/sonic-yang-mgmt/sonic_yang.py b/src/sonic-yang-mgmt/sonic_yang.py
index c1f2ddd1f573..6bc330c9ed6a 100644
--- a/src/sonic-yang-mgmt/sonic_yang.py
+++ b/src/sonic-yang-mgmt/sonic_yang.py
@@ -3,12 +3,14 @@
from json import dump
from glob import glob
-from datetime import datetime
+from _sonic_yang_ext import sonic_yang_ext_mixin
"""
Yang schema and data tree python APIs based on libyang python
+Here, sonic_yang_ext_mixin extends funtionality of sonic_yang,
+i.e. it is mixin not parent class.
"""
-class sonic_yang:
+class sonic_yang(sonic_yang_ext_mixin):
def __init__(self, yang_dir, debug=False):
self.yang_dir = yang_dir
@@ -58,11 +60,6 @@ def fail(self, e):
print(e)
raise e
- """
- import all function from extension file
- """
- from _sonic_yang_ext import *
-
"""
load_schema_module(): load a Yang model file
input: yang_file - full path of a Yang model file
@@ -240,7 +237,7 @@ def validate_data (self, node=None, ctx=None):
ctx = self.ctx
try:
- rc = node.validate(ly.LYD_OPT_CONFIG, ctx)
+ node.validate(ly.LYD_OPT_CONFIG, ctx)
except Exception as e:
self.fail(e)
@@ -377,7 +374,7 @@ def find_data_node_schema_xpath(self, data_xpath):
"""
def add_data_node(self, data_xpath, value):
try:
- data_node = self.new_data_node(data_xpath, value)
+ self.new_node(xpath, value)
#check if the node added to the data tree
self.find_data_node(data_xpath)
except Exception as e:
@@ -455,7 +452,7 @@ def find_data_node_value(self, data_xpath):
"""
def set_data_node_value(self, data_xpath, value):
try:
- data_node = self.root.new_path(self.ctx, data_xpath, str(value), ly.LYD_ANYDATA_STRING, ly.LYD_PATH_OPT_UPDATE)
+ self.root.new_path(self.ctx, data_xpath, str(value), ly.LYD_ANYDATA_STRING, ly.LYD_PATH_OPT_UPDATE)
except Exception as e:
print("set data node value failed for xpath: " + str(data_xpath))
self.fail(e)
@@ -477,7 +474,7 @@ def find_data_nodes(self, data_xpath):
raise Exception('data node not found')
for data_set in node_set.data():
- schema = data_set.schema()
+ data_set.schema()
list.append(data_set.path())
return list
@@ -489,7 +486,6 @@ def find_data_nodes(self, data_xpath):
"""
def find_schema_dependencies (self, schema_xpath):
ref_list = []
- node = self.root
try:
schema_node = self.find_schema_node(schema_xpath)
except Exception as e:
@@ -530,7 +526,7 @@ def find_data_dependencies (self, data_xpath):
for link in backlinks.schema():
node_set = node.find_path(link.path())
for data_set in node_set.data():
- schema = data_set.schema()
+ data_set.schema()
casted = data_set.subtype()
if value == casted.value_str():
ref_list.append(data_set.path())
@@ -651,7 +647,7 @@ def get_leafref_type_schema (self, schema_xpath):
if subtype.type().base() != ly.LY_TYPE_LEAFREF:
return None
else:
- leafref_path = subtype.type().info().lref().path()
+ subtype.type().info().lref().path()
target = subtype.type().info().lref().target()
target_path = target.path()
target_type = self.get_data_type(target_path)
diff --git a/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py b/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py
index aa98bdf149c8..6002b4386068 100644
--- a/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py
+++ b/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py
@@ -1,11 +1,8 @@
import sys
import os
import pytest
-import yang as ly
import sonic_yang as sy
import json
-import getopt
-import subprocess
import glob
import logging
from ijson import items as ijson_itmes
@@ -31,7 +28,6 @@ def data(self):
@pytest.fixture(autouse=True, scope='class')
def yang_s(self, data):
yang_dir = str(data['yang_dir'])
- data_file = str(data['data_file'])
yang_s = sy.sonic_yang(yang_dir)
return yang_s
@@ -62,7 +58,7 @@ def readIjsonInput(self, yang_test_file, test):
raise(e)
return jInput
- def setup_class(cls):
+ def setup_class(self):
pass
def load_yang_model_file(self, yang_s, yang_dir, yang_file, module_name):
@@ -140,14 +136,14 @@ def test_find_node(self, data, yang_s):
assert dnode is not None
assert dnode.path() == xpath
else:
- assert dnode == None
+ 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']
- status = yang_s.add_data_node(xpath, str(value))
+ yang_s.add_node(xpath, str(value))
data_node = yang_s.find_data_node(xpath)
assert data_node is not None
@@ -165,7 +161,6 @@ def test_find_data_node_value(self, data, yang_s):
#test delete data node
def test_delete_node(self, data, yang_s):
for node in data['delete_nodes']:
- expected = node['valid']
xpath = str(node['xpath'])
yang_s._delete_node(xpath)
@@ -291,5 +286,5 @@ def test_xlate_rev_xlate(self):
return
- def teardown_class(cls):
+ def teardown_class(self):
pass