Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions dump/plugins/fdb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from dump.match_infra import MatchRequest
from dump.helper import create_template_dict
from .executor import Executor


class Fdb(Executor):
"""
Debug Dump Plugin for FDB Module
"""
ARG_NAME = "Vlan:fdb_entry"

def __init__(self, match_engine=None):
super().__init__(match_engine)

def get_all_args(self, ns=""):
req = MatchRequest(db="STATE_DB", table="FDB_TABLE", key_pattern="*", ns=ns)
ret = self.match_engine.fetch(req)
fdb_entries = ret["keys"]
return [key.split("|")[-1] for key in fdb_entries]

def execute(self, params):
self.ret_temp = create_template_dict(dbs=["APPL_DB", "ASIC_DB", "STATE_DB"])
fdb_entry = params[Fdb.ARG_NAME]
self.ns = params["namespace"]
self.init_fdb_appl_info(fdb_entry)
self.init_asic_fdb_info(fdb_entry)
self.init_state_fdb_info(fdb_entry)
return self.ret_temp

def init_state_fdb_info(self, fdb_name):
req = MatchRequest(db="STATE_DB", table="FDB_TABLE", key_pattern=fdb_name, ns=self.ns)
ret = self.match_engine.fetch(req)
self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"])

def init_fdb_appl_info(self, fdb_name):
req = MatchRequest(db="APPL_DB", table="FDB_TABLE", key_pattern=fdb_name, ns=self.ns)
ret = self.match_engine.fetch(req)
self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"], False)
req = MatchRequest(db="APPL_DB", table="VXLAN_FDB_TABLE", key_pattern=fdb_name, ns=self.ns)
ret = self.match_engine.fetch(req)
self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"], False)
req = MatchRequest(db="APPL_DB", table="MCLAG_FDB_TABLE", key_pattern=fdb_name, ns=self.ns)
ret = self.match_engine.fetch(req)
self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"], False)

def init_asic_fdb_info(self, fdb_name):
key_split = fdb_name.split(":",1)
vlan_name = key_split[0]
mac = key_split[1]
if vlan_name[0:4] != "Vlan" or not vlan_name[4:].isnumeric():
self.ret_temp["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY")
self.ret_temp["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT")
return

vlan_num = int(vlan_name[4:])
# Find the table named "ASIC_STATE:SAI_OBJECT_TYPE_VLAN:*" in which SAI_VLAN_AT'TR_VLAN_ID = vlan_num
req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_VLAN", key_pattern="*", field="SAI_VLAN_ATTR_VLAN_ID",
value=str(vlan_num), ns=self.ns)
ret = self.match_engine.fetch(req)
if not ret["error"] and len(ret["keys"]) == 1:
vlan_obj = ret["keys"][0].split(":",2)[-1]
else:
self.ret_temp["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY")
self.ret_temp["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT")
return

# ASIC_DB FDB format is bvid:vlan_obj + mac:mac_address + switch id which is wildcard here
fdb_key = '{"bvid":"' + vlan_obj + '","mac":"' + mac.upper() + '"*}'
req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", key_pattern=fdb_key,
return_fields=["SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID"], ns=self.ns)
ret = self.match_engine.fetch(req)
bridge_port_id = ""
if not ret["error"] and len(ret["keys"]) != 0:
asic_fdb_key = ret["keys"][0]
if asic_fdb_key in ret["return_values"] and "SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID" in ret["return_values"][asic_fdb_key]:
bridge_port_id = ret["return_values"][asic_fdb_key]["SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID"]
else:
self.ret_temp["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT")

if bridge_port_id:
bridge_port_req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT",
key_pattern = bridge_port_id, ns = self.ns)
bridge_ret = self.match_engine.fetch(bridge_port_req)
if not bridge_ret["error"] and len(bridge_ret["keys"]) != 0:
self.ret_temp[bridge_port_req.db]["keys"].append(bridge_ret["keys"][0])
else:
self.ret_temp["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT")


self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"])
15 changes: 15 additions & 0 deletions tests/dump_input/fdb/appl_db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"FDB_TABLE:Vlan10:04:3f:72:ce:80:8b":{
"port" : "Ethernet252",
"type" : "static"
},
"VXLAN_FDB_TABLE:Vlan10:04:3f:72:ce:80:8c":{
"port" : "Ethernet252",
"type" : "dynamic",
"end_point_ip" : "192.234.11.233"
},
"MCLAG_FDB_TABLE:Vlan10:04:3f:72:ce:80:8d":{
"port" : "Ethernet252",
"type" : "dynamic"
}
}
48 changes: 48 additions & 0 deletions tests/dump_input/fdb/asic_db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d22\",\"mac\":\"04:3F:72:E3:70:08\",\"switch_id\":\"oid:0x21000000000000\"}":{
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID" : "oid:0x3a000000000d23",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC",
"SAI_FDB_ENTRY_ATTR_PACKET_ACTION" : "SAI_PACKET_ACTION_FORWARD"
},
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d20\",\"mac\":\"04:3F:72:CE:80:8B\",\"switch_id\":\"oid:0x21000000000000\"}":{
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID" : "oid:0x3a000000000d18",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_STATIC",
"SAI_FDB_ENTRY_ATTR_PACKET_ACTION" : "SAI_PACKET_ACTION_FORWARD"
},
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d20\",\"mac\":\"04:3F:72:CE:80:8C\",\"switch_id\":\"oid:0x21000000000000\"}":{
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID" : "oid:0x3a000000000d18",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC",
"SAI_FDB_ENTRY_ATTR_PACKET_ACTION" : "SAI_PACKET_ACTION_FORWARD"
},
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d20\",\"mac\":\"04:3F:72:CE:80:8D\",\"switch_id\":\"oid:0x21000000000000\"}":{
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID" : "oid:0x3a000000000d18",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC",
"SAI_FDB_ENTRY_ATTR_PACKET_ACTION" : "SAI_PACKET_ACTION_FORWARD"
},
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d1c\",\"mac\":\"04:3F:72:CE:80:8B\",\"switch_id\":\"oid:0x21000000000000\"}":{
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID" : "oid:0x3a000000000d55",
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC",
"SAI_FDB_ENTRY_ATTR_PACKET_ACTION" : "SAI_PACKET_ACTION_FORWARD"
},
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x26000000000d22": {
"SAI_VLAN_ATTR_VLAN_ID" : "50"
},
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x26000000000d20": {
"SAI_VLAN_ATTR_VLAN_ID" : "10"
},
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x26000000000d1c": {
"SAI_VLAN_ATTR_VLAN_ID" : "690"
},
"ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000d23":{
"SAI_BRIDGE_PORT_ATTR_TYPE" : "SAI_BRIDGE_PORT_TYPE_PORT",
"SAI_BRIDGE_PORT_ATTR_PORT_ID": "oid:0x100000000093e",
"SAI_BRIDGE_PORT_ATTR_ADMIN_STATE" : "true",
"SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE" : "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW"
},
"ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000d18":{
"SAI_BRIDGE_PORT_ATTR_TYPE" : "SAI_BRIDGE_PORT_TYPE_PORT",
"SAI_BRIDGE_PORT_ATTR_PORT_ID": "oid:0x100000000053f",
"SAI_BRIDGE_PORT_ATTR_ADMIN_STATE" : "true",
"SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE" : "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW"
}
}
30 changes: 30 additions & 0 deletions tests/dump_input/fdb/state_db.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"FDB_TABLE|Vlan50:04:3f:72:e3:70:08":{
"port" : "Ethernet0",
"type" : "dynamic"
},
"FDB_TABLE|Vlan10:04:3f:72:ce:80:8b":{
"port" : "Ethernet252",
"type" : "static"
},
"FDB_TABLE|Vlan10:04:3f:72:ce:80:8c":{
"port" : "Ethernet252",
"type" : "dynamic"
},
"FDB_TABLE|Vlan10:04:3f:72:ce:80:8d":{
"port" : "Ethernet252",
"type" : "dynamic"
},
"FDB_TABLE|Vlan690:04:3f:72:ce:80:8b":{
"port" : "Ethernet248",
"type" : "dynamic"
},
"FDB_TABLE|Vlan40:04:3f:72:e3:70:09":{
"port" : "PortChannel0002",
"type" : "dynamic"
},
"FDB_TABLE|Vlan691:04:3f:72:ce:80:8b":{
"port" : "Ethernet248",
"type" : "dynamic"
}
}
185 changes: 185 additions & 0 deletions tests/dump_tests/module_tests/fdb_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import json
import os
import sys
import jsonpatch
import unittest
import pytest
from deepdiff import DeepDiff
from mock import patch
from dump.helper import create_template_dict, populate_mock
from dump.plugins.fdb import Fdb
from dump.match_infra import MatchEngine, ConnectionPool
from swsscommon.swsscommon import SonicV2Connector

# Location for dedicated db's used for UT
module_tests_path = os.path.dirname(__file__)
dump_tests_path = os.path.join(module_tests_path, "../")
tests_path = os.path.join(dump_tests_path, "../")
dump_test_input = os.path.join(tests_path, "dump_input")
fdb_files_path = os.path.join(dump_test_input, "fdb")

# Define the mock files to read from
dedicated_dbs = {}
dedicated_dbs['APPL_DB'] = os.path.join(fdb_files_path, "appl_db.json")
dedicated_dbs['ASIC_DB'] = os.path.join(fdb_files_path, "asic_db.json")
dedicated_dbs['STATE_DB'] = os.path.join(fdb_files_path, "state_db.json")



@pytest.fixture(scope="class", autouse=True)
def match_engine():

print("SETUP")
os.environ["VERBOSE"] = "1"

# Monkey Patch the SonicV2Connector Object
from ...mock_tables import dbconnector
db = SonicV2Connector()

# popualate the db with mock data
db_names = list(dedicated_dbs.keys())
try:
populate_mock(db, db_names, dedicated_dbs)
except Exception as e:
assert False, "Mock initialization failed: " + str(e)

# Initialize connection pool
conn_pool = ConnectionPool()
DEF_NS = '' # Default Namespace
conn_pool.cache = {DEF_NS: {'conn': db,
'connected_to': set(db_names)}}

# Initialize match_engine
match_engine = MatchEngine(conn_pool)
yield match_engine
print("TEARDOWN")
os.environ["VERBOSE"] = "0"


@pytest.mark.usefixtures("match_engine")
class TestFdbModule:
def test_fdb_asic_learn_state(self, match_engine):
"""
Scenario: When FDB is learnt through hardware
"""
params = {Fdb.ARG_NAME: "Vlan50:04:3f:72:e3:70:08", "namespace": ""}
m_fdb = Fdb(match_engine)
returned = m_fdb.execute(params)
expect = create_template_dict(dbs=["APPL_DB", "ASIC_DB", "STATE_DB"])
expect["STATE_DB"]["keys"].append("FDB_TABLE|Vlan50:04:3f:72:e3:70:08")
expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000d23")
expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d22\",\"mac\":\"04:3F:72:E3:70:08\",\"switch_id\":\"oid:0x21000000000000\"}")
ddiff = DeepDiff(returned, expect, ignore_order=True)
assert not ddiff, ddiff

def test_fdb_app_program_state(self, match_engine):
"""
Scenario: When FDB is learnt through EVPN
"""
params = {Fdb.ARG_NAME: "Vlan10:04:3f:72:ce:80:8b", "namespace": ""}
m_fdb = Fdb(match_engine)
returned = m_fdb.execute(params)
expect = create_template_dict(dbs=["APPL_DB", "ASIC_DB", "STATE_DB"])
expect["APPL_DB"]["keys"].append("FDB_TABLE:Vlan10:04:3f:72:ce:80:8b")
expect["STATE_DB"]["keys"].append("FDB_TABLE|Vlan10:04:3f:72:ce:80:8b")
expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000d18")
expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d20\",\"mac\":\"04:3F:72:CE:80:8B\",\"switch_id\":\"oid:0x21000000000000\"}")
ddiff = DeepDiff(returned, expect, ignore_order=True)
assert not ddiff, ddiff

def test_fdb_evpn_learn_state(self, match_engine):
"""
Scenario: When FDB is learnt through EVPN
"""
params = {Fdb.ARG_NAME: "Vlan10:04:3f:72:ce:80:8c", "namespace": ""}
m_fdb = Fdb(match_engine)
returned = m_fdb.execute(params)
expect = create_template_dict(dbs=["APPL_DB", "ASIC_DB", "STATE_DB"])
expect["APPL_DB"]["keys"].append("VXLAN_FDB_TABLE:Vlan10:04:3f:72:ce:80:8c")
expect["STATE_DB"]["keys"].append("FDB_TABLE|Vlan10:04:3f:72:ce:80:8c")
expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000d18")
expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d20\",\"mac\":\"04:3F:72:CE:80:8C\",\"switch_id\":\"oid:0x21000000000000\"}")
ddiff = DeepDiff(returned, expect, ignore_order=True)
assert not ddiff, ddiff

def test_fdb_mclag_sync_state(self, match_engine):
"""
Scenario: When FDB is learnt through EVPN
"""
params = {Fdb.ARG_NAME: "Vlan10:04:3f:72:ce:80:8d", "namespace": ""}
m_fdb = Fdb(match_engine)
returned = m_fdb.execute(params)
expect = create_template_dict(dbs=["APPL_DB", "ASIC_DB", "STATE_DB"])
expect["APPL_DB"]["keys"].append("MCLAG_FDB_TABLE:Vlan10:04:3f:72:ce:80:8d")
expect["STATE_DB"]["keys"].append("FDB_TABLE|Vlan10:04:3f:72:ce:80:8d")
expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000000d18")
expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d20\",\"mac\":\"04:3F:72:CE:80:8D\",\"switch_id\":\"oid:0x21000000000000\"}")
ddiff = DeepDiff(returned, expect, ignore_order=True)
assert not ddiff, ddiff

def test_missing_asic_bridgeport(self, match_engine):
"""
Scenario: When FDB is learnt through hardware but bridge port is missing
"""
params = {Fdb.ARG_NAME: "Vlan690:04:3f:72:ce:80:8b", "namespace": ""}
m_fdb = Fdb(match_engine)
returned = m_fdb.execute(params)
expect = create_template_dict(dbs=["APPL_DB", "ASIC_DB", "STATE_DB"])
expect["STATE_DB"]["keys"].append("FDB_TABLE|Vlan690:04:3f:72:ce:80:8b")
expect["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT")
expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x26000000000d1c\",\"mac\":\"04:3F:72:CE:80:8B\",\"switch_id\":\"oid:0x21000000000000\"}")
ddiff = DeepDiff(returned, expect, ignore_order=True)
assert not ddiff, ddiff

def test_missing_asic_vlan(self, match_engine):
"""
Scenario: When FDB is learnt through hardware but vlan is missing
"""
params = {Fdb.ARG_NAME: "Vlan40:04:3f:72:e3:70:09", "namespace": ""}
m_fdb = Fdb(match_engine)
returned = m_fdb.execute(params)
expect = create_template_dict(dbs=["APPL_DB", "ASIC_DB", "STATE_DB"])
expect["STATE_DB"]["keys"].append("FDB_TABLE|Vlan40:04:3f:72:e3:70:09")
expect["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT")
expect["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY")
ddiff = DeepDiff(returned, expect, ignore_order=True)
assert not ddiff, ddiff

def test_missing_asic(self, match_engine):
"""
Scenario: When FDB is learnt through hardware but asic db is deleted
"""
params = {Fdb.ARG_NAME: "Vlan691:04:3f:72:ce:80:8b", "namespace": ""}
m_fdb = Fdb(match_engine)
returned = m_fdb.execute(params)
expect = create_template_dict(dbs=["APPL_DB", "ASIC_DB", "STATE_DB"])
expect["STATE_DB"]["keys"].append("FDB_TABLE|Vlan691:04:3f:72:ce:80:8b")
expect["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT")
expect["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY")
ddiff = DeepDiff(returned, expect, ignore_order=True)
assert not ddiff, ddiff

def test_no_fdb(self, match_engine):
"""
Scenario: When no entry for the fdb is present in any of the db's
"""
params = {Fdb.ARG_NAME: "Vlan691:04:3f:72:ce:80:8c", "namespace": ""}
m_fdb = Fdb(match_engine)
returned = m_fdb.execute(params)
expect = create_template_dict(dbs=["APPL_DB", "ASIC_DB", "STATE_DB"])
expect["STATE_DB"]["tables_not_found"].append("FDB_TABLE")
expect["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT")
expect["ASIC_DB"]["tables_not_found"].append("ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY")
ddiff = DeepDiff(returned, expect, ignore_order=True)
assert not ddiff, ddiff

def test_all_args(self, match_engine):
"""
Scenario: Verify Whether the get_all_args method is working as expected
"""
params = {}
m_fdb = Fdb(match_engine)
returned = m_fdb.get_all_args("")
expect = ["Vlan50:04:3f:72:e3:70:08", "Vlan10:04:3f:72:ce:80:8b", "Vlan10:04:3f:72:ce:80:8c", "Vlan10:04:3f:72:ce:80:8d", "Vlan690:04:3f:72:ce:80:8b", "Vlan40:04:3f:72:e3:70:09", "Vlan691:04:3f:72:ce:80:8b"]
ddiff = DeepDiff(expect, returned, ignore_order=True)
assert not ddiff, ddiff