Skip to content

Commit 5053943

Browse files
committed
[strict yang] Add YANG enforcemnet for CONFIG_DB tables set ops
1 parent 9512de5 commit 5053943

File tree

2 files changed

+104
-49
lines changed

2 files changed

+104
-49
lines changed

common/configdb.h

+45
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ func NewConfigDBConnector(a ...interface{}) *ConfigDBConnector {
7474
## Note: callback is difficult to implement by SWIG C++, so keep in python
7575
self.handlers = {}
7676
self.fire_init_data = {}
77+
self.supported_tables = []
7778

7879
def __enter__(self):
7980
return self
@@ -216,14 +217,57 @@ func NewConfigDBConnector(a ...interface{}) *ConfigDBConnector {
216217
if table in self.handlers:
217218
self.handlers.pop(table)
218219

220+
def load_yang_tables(self):
221+
if not self.supported_tables:
222+
import sonic_yang
223+
sy = sonic_yang.SonicYang("/usr/local/yang-models")
224+
sy.loadYangModel()
225+
yang_tables = [k for k, v in sy.confDbYangMap.items() if "container" in v]
226+
non_yang_tables = [
227+
"DEBUG_COUNTER",
228+
"DEBUG_COUNTER_DROP_REASON",
229+
"DTEL",
230+
"DTEL_REPORT_SESSION",
231+
"DTEL_INT_SESSION",
232+
"DTEL_QUEUE_REPORT",
233+
"DTEL_EVENT",
234+
"FDB",
235+
"GEARBOX",
236+
"LOGGING",
237+
"PASS_THROUGH_ROUTE_TABLE",
238+
"SEND_TO_INGRESS_PORT",
239+
"STP",
240+
"STP_PORT",
241+
"STP_VLAN_PORT",
242+
"STP_VLAN",
243+
"TC_TO_DOT1P_MAP",
244+
"TWAMP_SESSION",
245+
"VNET_ROUTE",
246+
"VNET_ROUTE_TUNNEL",
247+
"VRRP",
248+
"VRRP6",
249+
"WATERMARK_TABLE"
250+
]
251+
self.supported_tables = yang_tables + non_yang_tables
252+
253+
def check_table_support(self, table):
254+
if self.db_name != "CONFIG_DB":
255+
return
256+
257+
self.load_yang_tables()
258+
if table not in self.supported_tables:
259+
raise ValueError("{} is not supported due to missing YANG model".format(table))
260+
219261
def set_entry(self, table, key, data):
262+
self.check_table_support(table)
220263
key = self.serialize_key(key)
221264
raw_data = self.typed_to_raw(data)
222265
super(ConfigDBConnector, self).set_entry(table, key, raw_data)
223266

224267
def mod_config(self, data):
225268
raw_config = {}
226269
for table_name, table_data in data.items():
270+
self.check_table_support(table_name)
227271
if table_data == {}:
228272
# When table data is {}, no action.
229273
continue
@@ -238,6 +282,7 @@ func NewConfigDBConnector(a ...interface{}) *ConfigDBConnector {
238282
super(ConfigDBConnector, self).mod_config(raw_config)
239283

240284
def mod_entry(self, table, key, data):
285+
self.check_table_support(table)
241286
key = self.serialize_key(key)
242287
raw_data = self.typed_to_raw(data)
243288
super(ConfigDBConnector, self).mod_entry(table, key, raw_data)

tests/test_redis_ut.py

+59-49
Original file line numberDiff line numberDiff line change
@@ -375,16 +375,16 @@ def test_ConfigDBConnector():
375375
assert config_db.db_name == "CONFIG_DB"
376376
assert config_db.TABLE_NAME_SEPARATOR == "|"
377377
config_db.get_redis_client(config_db.CONFIG_DB).flushdb()
378-
config_db.set_entry("TEST_PORT", "Ethernet111", {"alias": "etp1x"})
378+
config_db.set_entry("PORT", "Ethernet111", {"alias": "etp1x"})
379379
allconfig = config_db.get_config()
380-
assert allconfig["TEST_PORT"]["Ethernet111"]["alias"] == "etp1x"
380+
assert allconfig["PORT"]["Ethernet111"]["alias"] == "etp1x"
381381

382-
config_db.set_entry("TEST_PORT", "Ethernet111", {"mtu": "12345"})
382+
config_db.set_entry("PORT", "Ethernet111", {"mtu": "12345"})
383383
allconfig = config_db.get_config()
384-
assert "alias" not in allconfig["TEST_PORT"]["Ethernet111"]
385-
assert allconfig["TEST_PORT"]["Ethernet111"]["mtu"] == "12345"
384+
assert "alias" not in allconfig["PORT"]["Ethernet111"]
385+
assert allconfig["PORT"]["Ethernet111"]["mtu"] == "12345"
386386

387-
config_db.delete_table("TEST_PORT")
387+
config_db.delete_table("PORT")
388388
allconfig = config_db.get_config()
389389
assert len(allconfig) == 0
390390

@@ -418,27 +418,27 @@ def test_ConfigDBPipeConnector():
418418
#
419419

420420
# Verify entry set
421-
config_db.set_entry("PORT_TABLE", "Ethernet1", {"alias": "etp1x"})
421+
config_db.set_entry("PORT", "Ethernet1", {"alias": "etp1x"})
422422
allconfig = config_db.get_config()
423-
assert allconfig["PORT_TABLE"]["Ethernet1"]["alias"] == "etp1x"
423+
assert allconfig["PORT"]["Ethernet1"]["alias"] == "etp1x"
424424

425425
config_db.set_entry("ACL_TABLE", "EVERFLOW", {"ports": ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"]})
426426
allconfig = config_db.get_config()
427427
assert allconfig["ACL_TABLE"]["EVERFLOW"]["ports"] == ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"]
428428

429429
# Verify entry update
430-
config_db.set_entry("PORT_TABLE", "Ethernet1", {"mtu": "12345"})
430+
config_db.set_entry("PORT", "Ethernet1", {"mtu": "12345"})
431431
allconfig = config_db.get_config()
432-
assert "alias" not in allconfig["PORT_TABLE"]["Ethernet1"]
433-
assert allconfig["PORT_TABLE"]["Ethernet1"]["mtu"] == "12345"
432+
assert "alias" not in allconfig["PORT"]["Ethernet1"]
433+
assert allconfig["PORT"]["Ethernet1"]["mtu"] == "12345"
434434

435435
# Verify entry clear
436-
config_db.set_entry("PORT_TABLE", "Ethernet1", {})
436+
config_db.set_entry("PORT", "Ethernet1", {})
437437
allconfig = config_db.get_config()
438-
assert len(allconfig["PORT_TABLE"]["Ethernet1"]) == 0
438+
assert len(allconfig["PORT"]["Ethernet1"]) == 0
439439

440440
# Verify entry delete
441-
config_db.set_entry("PORT_TABLE", "Ethernet1", None)
441+
config_db.set_entry("PORT", "Ethernet1", None)
442442
config_db.set_entry("ACL_TABLE", "EVERFLOW", None)
443443
allconfig = config_db.get_config()
444444
assert len(allconfig) == 0
@@ -448,17 +448,17 @@ def test_ConfigDBPipeConnector():
448448
#
449449

450450
# Verify entry set
451-
allconfig.setdefault("PORT_TABLE", {}).setdefault("Ethernet1", {})
452-
allconfig["PORT_TABLE"]["Ethernet1"]["alias"] = "etp1x"
451+
allconfig.setdefault("PORT", {}).setdefault("Ethernet1", {})
452+
allconfig["PORT"]["Ethernet1"]["alias"] = "etp1x"
453453
config_db.mod_config(allconfig)
454454
allconfig = config_db.get_config()
455-
assert allconfig["PORT_TABLE"]["Ethernet1"]["alias"] == "etp1x"
455+
assert allconfig["PORT"]["Ethernet1"]["alias"] == "etp1x"
456456

457-
allconfig.setdefault("VLAN_TABLE", {})
458-
allconfig["VLAN_TABLE"]["Vlan1"] = {}
457+
allconfig.setdefault("VLAN", {})
458+
allconfig["VLAN"]["Vlan1"] = {}
459459
config_db.mod_config(allconfig)
460460
allconfig = config_db.get_config()
461-
assert len(allconfig["VLAN_TABLE"]["Vlan1"]) == 0
461+
assert len(allconfig["VLAN"]["Vlan1"]) == 0
462462

463463
allconfig.setdefault("ACL_TABLE", {}).setdefault("EVERFLOW", {})
464464
allconfig["ACL_TABLE"]["EVERFLOW"]["ports"] = ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"]
@@ -467,8 +467,8 @@ def test_ConfigDBPipeConnector():
467467
assert allconfig["ACL_TABLE"]["EVERFLOW"]["ports"] == ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"]
468468

469469
# Verify entry delete
470-
allconfig["PORT_TABLE"]["Ethernet1"] = None
471-
allconfig["VLAN_TABLE"]["Vlan1"] = None
470+
allconfig["PORT"]["Ethernet1"] = None
471+
allconfig["VLAN"]["Vlan1"] = None
472472
allconfig["ACL_TABLE"]["EVERFLOW"] = None
473473
config_db.mod_config(allconfig)
474474
allconfig = config_db.get_config()
@@ -477,20 +477,20 @@ def test_ConfigDBPipeConnector():
477477
# Verify table delete
478478
for i in range(1, 1001, 1):
479479
# Make sure we have enough entries to trigger REDIS_SCAN_BATCH_SIZE
480-
allconfig.setdefault("PORT_TABLE", {}).setdefault("Ethernet{}".format(i), {})
481-
allconfig["PORT_TABLE"]["Ethernet{}".format(i)]["alias"] = "etp{}x".format(i)
480+
allconfig.setdefault("PORT", {}).setdefault("Ethernet{}".format(i), {})
481+
allconfig["PORT"]["Ethernet{}".format(i)]["alias"] = "etp{}x".format(i)
482482

483483
config_db.mod_config(allconfig)
484484
allconfig = config_db.get_config()
485-
assert len(allconfig["PORT_TABLE"]) == 1000
485+
assert len(allconfig["PORT"]) == 1000
486486

487487
# Verify modify config with {} will no action
488-
config_db.mod_config({'PORT_TABLE':{}})
488+
config_db.mod_config({'PORT':{}})
489489
allconfig = config_db.get_config()
490-
assert len(allconfig["PORT_TABLE"]) == 1000
490+
assert len(allconfig["PORT"]) == 1000
491491

492492
# Verify modify config with None will delete table
493-
allconfig["PORT_TABLE"] = None
493+
allconfig["PORT"] = None
494494
config_db.mod_config(allconfig)
495495
allconfig = config_db.get_config()
496496
assert len(allconfig) == 0
@@ -514,16 +514,16 @@ def test_ConfigDBPipeConnectorSeparator():
514514
config_db = ConfigDBPipeConnector()
515515
config_db.db_connect("CONFIG_DB", False, False)
516516
config_db.get_redis_client(config_db.CONFIG_DB).flushdb()
517-
config_db.set_entry("TEST_PORT", "Ethernet222", {"alias": "etp2x"})
517+
config_db.set_entry("PORT", "Ethernet222", {"alias": "etp2x"})
518518
db.set("ItemWithoutSeparator", "item11")
519519
allconfig = config_db.get_config()
520-
assert "TEST_PORT" in allconfig
520+
assert "PORT" in allconfig
521521
assert "ItemWithoutSeparator" not in allconfig
522522

523523
alltable = config_db.get_table("*")
524524
assert "Ethernet222" in alltable
525525

526-
config_db.delete_table("TEST_PORT")
526+
config_db.delete_table("PORT")
527527
db.delete("ItemWithoutSeparator")
528528
allconfig = config_db.get_config()
529529
assert len(allconfig) == 0
@@ -532,10 +532,11 @@ def test_ConfigDBScan():
532532
config_db = ConfigDBPipeConnector()
533533
config_db.connect(wait_for_init=False)
534534
config_db.get_redis_client(config_db.CONFIG_DB).flushdb()
535-
n = 1000
535+
tables = ["CRM", "DEVICE_METADATA", "FEATURE", "PORT", "SNMP"]
536+
n = len(tables)
536537
for i in range(0, n):
537538
s = str(i)
538-
config_db.mod_entry("TEST_TYPE" + s, "Ethernet" + s, {"alias" + s: "etp" + s})
539+
config_db.mod_entry(tables[i], "key" + s, {"alias" + s: "etp" + s})
539540

540541
allconfig = config_db.get_config()
541542
assert len(allconfig) == n
@@ -547,12 +548,21 @@ def test_ConfigDBScan():
547548

548549
for i in range(0, n):
549550
s = str(i)
550-
config_db.delete_table("TEST_TYPE" + s)
551+
config_db.delete_table(tables[i])
552+
553+
554+
def test_ConfigDB_non_yang_table_raise_error():
555+
config_db = ConfigDBConnector()
556+
config_db.connect(wait_for_init=False)
557+
with pytest.raises(ValueError) as excinfo:
558+
config_db.set_entry("TEST_PORT", "Ethernet111", {"alias": "etp1x"})
559+
assert str(excinfo.value) == "TEST_PORT is not supported due to missing YANG model"
560+
551561

552562
def test_ConfigDBFlush():
553563
config_db = ConfigDBConnector()
554564
config_db.connect(wait_for_init=False)
555-
config_db.set_entry("TEST_PORT", "Ethernet111", {"alias": "etp1x"})
565+
config_db.set_entry("PORT", "Ethernet111", {"alias": "etp1x"})
556566
client = config_db.get_redis_client(config_db.CONFIG_DB)
557567

558568
assert ConfigDBConnector.INIT_INDICATOR == "CONFIG_DB_INITIALIZED"
@@ -585,7 +595,7 @@ def test_multidb_ConfigDBConnector():
585595

586596

587597
def test_ConfigDBSubscribe():
588-
table_name = 'TEST_TABLE'
598+
table_name = 'PORT'
589599
test_key = 'key1'
590600
test_data = {'field1': 'value1'}
591601
global output_data
@@ -642,9 +652,9 @@ def thread_coming_entry():
642652
assert table_name not in config_db.handlers
643653

644654
def test_ConfigDBInit():
645-
table_name_1 = 'TEST_TABLE_1'
646-
table_name_2 = 'TEST_TABLE_2'
647-
table_name_3 = 'TEST_TABLE_3'
655+
table_name_1 = 'DEVICE_METADATA'
656+
table_name_2 = 'FEATURE'
657+
table_name_3 = 'PORT'
648658
test_key = 'key1'
649659
test_data = {'field1': 'value1', 'field2': 'value2'}
650660

@@ -777,9 +787,9 @@ def test_ConfigDBWaitInit():
777787
config_db = ConfigDBConnector()
778788
config_db.db_connect(config_db.CONFIG_DB, True, False)
779789

780-
config_db.set_entry("TEST_PORT", "Ethernet111", {"alias": "etp1x"})
790+
config_db.set_entry("PORT", "Ethernet111", {"alias": "etp1x"})
781791
allconfig = config_db.get_config()
782-
assert allconfig["TEST_PORT"]["Ethernet111"]["alias"] == "etp1x"
792+
assert allconfig["PORT"]["Ethernet111"]["alias"] == "etp1x"
783793

784794

785795
def test_ConfigDBConnector():
@@ -795,20 +805,20 @@ def test_ConfigDBConnector():
795805
allconfig = config_db.get_config()
796806
for i in range(1, 1001, 1):
797807
# Make sure we have enough entries to trigger REDIS_SCAN_BATCH_SIZE
798-
allconfig.setdefault("PORT_TABLE", {}).setdefault("Ethernet{}".format(i), {})
799-
allconfig["PORT_TABLE"]["Ethernet{}".format(i)]["alias"] = "etp{}x".format(i)
808+
allconfig.setdefault("PORT", {}).setdefault("Ethernet{}".format(i), {})
809+
allconfig["PORT"]["Ethernet{}".format(i)]["alias"] = "etp{}x".format(i)
800810

801811
config_db.mod_config(allconfig)
802812
allconfig = config_db.get_config()
803-
assert len(allconfig["PORT_TABLE"]) == 1000
813+
assert len(allconfig["PORT"]) == 1000
804814

805815
# Verify modify config with {} will no action
806-
config_db.mod_config({'PORT_TABLE':{}})
816+
config_db.mod_config({'PORT':{}})
807817
allconfig = config_db.get_config()
808-
assert len(allconfig["PORT_TABLE"]) == 1000
818+
assert len(allconfig["PORT"]) == 1000
809819

810820
# Verify modify config with None will delete table
811-
allconfig["PORT_TABLE"] = None
821+
allconfig["PORT"] = None
812822
config_db.mod_config(allconfig)
813823
allconfig = config_db.get_config()
814824
assert len(allconfig) == 0
@@ -824,9 +834,9 @@ def test_ConfigDBConnector_with_statement(self):
824834
assert config_db.db_name == "CONFIG_DB"
825835
assert config_db.TABLE_NAME_SEPARATOR == "|"
826836
config_db.get_redis_client(config_db.CONFIG_DB).flushdb()
827-
config_db.set_entry("TEST_PORT", "Ethernet111", {"alias": "etp1x"})
837+
config_db.set_entry("PORT", "Ethernet111", {"alias": "etp1x"})
828838
allconfig = config_db.get_config()
829-
assert allconfig["TEST_PORT"]["Ethernet111"]["alias"] == "etp1x"
839+
assert allconfig["PORT"]["Ethernet111"]["alias"] == "etp1x"
830840

831841
# check close() method called by with statement
832842
ConfigDBConnector.close.assert_called_once_with()

0 commit comments

Comments
 (0)