Skip to content

Commit 11335b2

Browse files
author
Ubuntu
committed
add yang validation service
1 parent 2a8c285 commit 11335b2

File tree

2 files changed

+63
-10
lines changed

2 files changed

+63
-10
lines changed

config/main.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from utilities_common.general import load_db_config
3131

3232
from .utils import log
33+
from .yang_validation_service import YangValidationService
3334

3435
from . import aaa
3536
from . import chassis_modules
@@ -1753,10 +1754,6 @@ def portchannel(ctx, namespace):
17531754
@click.pass_context
17541755
def add_portchannel(ctx, portchannel_name, min_links, fallback):
17551756
"""Add port channel"""
1756-
if is_portchannel_name_valid(portchannel_name) != True:
1757-
ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'"
1758-
.format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO))
1759-
17601757
db = ctx.obj['db']
17611758

17621759
if is_portchannel_present_in_db(db, portchannel_name):
@@ -1769,17 +1766,18 @@ def add_portchannel(ctx, portchannel_name, min_links, fallback):
17691766
fvs['min_links'] = str(min_links)
17701767
if fallback != 'false':
17711768
fvs['fallback'] = 'true'
1772-
db.set_entry('PORTCHANNEL', portchannel_name, fvs)
1769+
yvs = YangValidationService()
1770+
if not yvs.validate_set_entry('PORTCHANNEL', portchannel_name, fvs):
1771+
ctx.fail("Invalid configuration based on PortChannel YANG model")
1772+
else:
1773+
db.set_entry('PORTCHANNEL', portchannel_name, fvs)
1774+
17731775

17741776
@portchannel.command('del')
17751777
@click.argument('portchannel_name', metavar='<portchannel_name>', required=True)
17761778
@click.pass_context
17771779
def remove_portchannel(ctx, portchannel_name):
17781780
"""Remove port channel"""
1779-
if is_portchannel_name_valid(portchannel_name) != True:
1780-
ctx.fail("{} is invalid!, name should have prefix '{}' and suffix '{}'"
1781-
.format(portchannel_name, CFG_PORTCHANNEL_PREFIX, CFG_PORTCHANNEL_NO))
1782-
17831781
db = ctx.obj['db']
17841782

17851783
# Dont proceed if the port channel does not exist
@@ -1789,7 +1787,11 @@ def remove_portchannel(ctx, portchannel_name):
17891787
if len([(k, v) for k, v in db.get_table('PORTCHANNEL_MEMBER') if k == portchannel_name]) != 0:
17901788
click.echo("Error: Portchannel {} contains members. Remove members before deleting Portchannel!".format(portchannel_name))
17911789
else:
1792-
db.set_entry('PORTCHANNEL', portchannel_name, None)
1790+
yvs = YangValidationService()
1791+
if not yvs.validate_set_entry('PORTCHANNEL', portchannel_name, None):
1792+
ctx.fail("Invalid configuration based on PortChannel YANG model")
1793+
else:
1794+
db.set_entry('PORTCHANNEL', portchannel_name, None)
17931795

17941796
@portchannel.group(cls=clicommon.AbbreviationGroup, name='member')
17951797
@click.pass_context

config/yang_validation_service.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import json
2+
import sonic_yang
3+
import subprocess
4+
import copy
5+
6+
YANG_DIR = "/usr/local/yang-models"
7+
8+
class YangValidationService:
9+
def __init__(self, yang_dir = YANG_DIR):
10+
self.yang_dir = YANG_DIR
11+
self.sonic_yang_with_loaded_models = None
12+
13+
def get_config_db_as_json(self):
14+
cmd = "show runningconfiguration all"
15+
result = subprocess.Popen(cmd, shell=True, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
16+
text, err = result.communicate()
17+
return_code = result.returncode
18+
if return_code:
19+
raise RuntimeError("Failed to get running config, Return Code: {}, Error: {}".format(return_code, err))
20+
return json.loads(text)
21+
22+
def create_sonic_yang_with_loaded_models(self):
23+
if self.sonic_yang_with_loaded_models is None:
24+
loaded_models_sy = sonic_yang.SonicYang(self.yang_dir)
25+
loaded_models_sy.loadYangModel()
26+
self.sonic_yang_with_loaded_models = loaded_models_sy
27+
28+
return copy.copy(self.sonic_yang_with_loaded_models)
29+
30+
def validate_config_db_config(self, config_json):
31+
sy = self.create_sonic_yang_with_loaded_models()
32+
try:
33+
tmp_config_json = copy.deepcopy(config_json)
34+
sy.loadData(tmp_config_json)
35+
sy.validate_data_tree()
36+
return True
37+
except sonic_yang.SonicYangException as ex:
38+
return False
39+
return False
40+
41+
def validate_set_entry(self, table, key, data):
42+
config_json = self.get_config_db_as_json()
43+
if not self.validate_config_db_config(config_json):
44+
return False
45+
if data is not None:
46+
config_json[table][key] = data
47+
if not self.validate_config_db_config(config_json):
48+
return False
49+
return True
50+
51+

0 commit comments

Comments
 (0)