Skip to content

Commit e9cfb32

Browse files
authored
[fast-reboot] Fix dump script to support PortChannels in a VLAN group (sonic-net#1393)
Add PortChannels to the list of interfaces (port_id_2_iface) to support FDB dump for PortChannel in a VLAN group. Fix sonic-net#4793 - How I did it - Get LAG ID from the DB. - Find the LAG name from APP DB. - Add it to the list of 'port_id_2_iface' to be used. - How to verify it Reproduce the issue mentioned on this PR and try to run fast-reboot with this fix. - Previous command output (if the output of a command-line utility has changed) Traceback: src_ifs = {map_mac_ip_per_vlan[vlan_name][dst_mac] for vlan_name, dst_mac, _ in arp_entries} KeyError: 'b8:59:9f:a8:e2:00' Signed-off-by: Shlomi Bitton <[email protected]>
1 parent 9a2872d commit e9cfb32

File tree

5 files changed

+187
-24
lines changed

5 files changed

+187
-24
lines changed

scripts/fast-reboot-dump.py

+64-24
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,7 @@ def generate_neighbor_entries(filename, all_available_macs):
3939
}
4040
arp_output.append(obj)
4141

42-
ip_addr = key.split(':')[2]
43-
if ipaddress.ip_interface(str(ip_addr)).ip.version != 4:
44-
#This is ipv6 address
45-
ip_addr = key.replace(key.split(':')[0] + ':' + key.split(':')[1] + ':', '')
42+
ip_addr = key.split(':', 2)[2]
4643
neighbor_entries.append((vlan_name, mac, ip_addr))
4744
syslog.syslog(syslog.LOG_INFO, "Neighbor entry: [Vlan: %s, Mac: %s, Ip: %s]" % (vlan_name, mac, ip_addr))
4845

@@ -80,23 +77,58 @@ def get_bridge_port_id_2_port_id(db):
8077

8178
return bridge_port_id_2_port_id
8279

83-
def get_map_port_id_2_iface_name(db):
84-
port_id_2_iface = {}
85-
keys = db.keys(db.ASIC_DB, 'ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF:oid:*')
80+
def get_lag_by_member(member_name, app_db):
81+
keys = app_db.keys(app_db.APPL_DB, 'LAG_MEMBER_TABLE:*')
8682
keys = [] if keys is None else keys
8783
for key in keys:
88-
value = db.get_all(db.ASIC_DB, key)
84+
_, lag_name, lag_member_name = key.split(":")
85+
if lag_member_name == member_name:
86+
return lag_name
87+
return None
88+
89+
def get_map_host_port_id_2_iface_name(asic_db):
90+
host_port_id_2_iface = {}
91+
keys = asic_db.keys(asic_db.ASIC_DB, 'ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF:oid:*')
92+
keys = [] if keys is None else keys
93+
for key in keys:
94+
value = asic_db.get_all(asic_db.ASIC_DB, key)
8995
if value['SAI_HOSTIF_ATTR_TYPE'] != 'SAI_HOSTIF_TYPE_NETDEV':
9096
continue
9197
port_id = value['SAI_HOSTIF_ATTR_OBJ_ID']
9298
iface_name = value['SAI_HOSTIF_ATTR_NAME']
93-
port_id_2_iface[port_id] = iface_name
99+
host_port_id_2_iface[port_id] = iface_name
100+
101+
return host_port_id_2_iface
102+
103+
def get_map_lag_port_id_2_portchannel_name(asic_db, app_db, host_port_id_2_iface):
104+
lag_port_id_2_iface = {}
105+
keys = asic_db.keys(asic_db.ASIC_DB, 'ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER:oid:*')
106+
keys = [] if keys is None else keys
107+
for key in keys:
108+
value = asic_db.get_all(asic_db.ASIC_DB, key)
109+
lag_id = value['SAI_LAG_MEMBER_ATTR_LAG_ID']
110+
if lag_id in lag_port_id_2_iface:
111+
continue
112+
member_id = value['SAI_LAG_MEMBER_ATTR_PORT_ID']
113+
member_name = host_port_id_2_iface[member_id]
114+
lag_name = get_lag_by_member(member_name, app_db)
115+
if lag_name is not None:
116+
lag_port_id_2_iface[lag_id] = lag_name
117+
118+
return lag_port_id_2_iface
119+
120+
def get_map_port_id_2_iface_name(asic_db, app_db):
121+
port_id_2_iface = {}
122+
host_port_id_2_iface = get_map_host_port_id_2_iface_name(asic_db)
123+
port_id_2_iface.update(host_port_id_2_iface)
124+
lag_port_id_2_iface = get_map_lag_port_id_2_portchannel_name(asic_db, app_db, host_port_id_2_iface)
125+
port_id_2_iface.update(lag_port_id_2_iface)
94126

95127
return port_id_2_iface
96128

97-
def get_map_bridge_port_id_2_iface_name(db):
98-
bridge_port_id_2_port_id = get_bridge_port_id_2_port_id(db)
99-
port_id_2_iface = get_map_port_id_2_iface_name(db)
129+
def get_map_bridge_port_id_2_iface_name(asic_db, app_db):
130+
bridge_port_id_2_port_id = get_bridge_port_id_2_port_id(asic_db)
131+
port_id_2_iface = get_map_port_id_2_iface_name(asic_db, app_db)
100132

101133
bridge_port_id_2_iface_name = {}
102134

@@ -158,29 +190,37 @@ def get_fdb(db, vlan_name, vlan_id, bridge_id_2_iface):
158190
return fdb_entries, available_macs, map_mac_ip
159191

160192
def generate_fdb_entries(filename):
161-
fdb_entries = []
193+
asic_db = swsssdk.SonicV2Connector(host='127.0.0.1')
194+
app_db = swsssdk.SonicV2Connector(host='127.0.0.1')
195+
asic_db.connect(asic_db.ASIC_DB, False) # Make one attempt only
196+
app_db.connect(app_db.APPL_DB, False) # Make one attempt only
162197

163-
db = SonicV2Connector(use_unix_socket_path=False)
164-
db.connect(db.ASIC_DB, False) # Make one attempt only
198+
vlan_ifaces = get_vlan_ifaces()
165199

166-
bridge_id_2_iface = get_map_bridge_port_id_2_iface_name(db)
200+
fdb_entries, all_available_macs, map_mac_ip_per_vlan = generate_fdb_entries_logic(asic_db, app_db, vlan_ifaces)
167201

168-
vlan_ifaces = get_vlan_ifaces()
202+
asic_db.close(asic_db.ASIC_DB)
203+
app_db.close(app_db.APPL_DB)
204+
205+
with open(filename, 'w') as fp:
206+
json.dump(fdb_entries, fp, indent=2, separators=(',', ': '))
169207

208+
return all_available_macs, map_mac_ip_per_vlan
209+
210+
def generate_fdb_entries_logic(asic_db, app_db, vlan_ifaces):
211+
fdb_entries = []
170212
all_available_macs = set()
171213
map_mac_ip_per_vlan = {}
214+
215+
bridge_id_2_iface = get_map_bridge_port_id_2_iface_name(asic_db, app_db)
216+
172217
for vlan in vlan_ifaces:
173218
vlan_id = int(vlan.replace('Vlan', ''))
174-
fdb_entry, available_macs, map_mac_ip_per_vlan[vlan] = get_fdb(db, vlan, vlan_id, bridge_id_2_iface)
219+
fdb_entry, available_macs, map_mac_ip_per_vlan[vlan] = get_fdb(asic_db, vlan, vlan_id, bridge_id_2_iface)
175220
all_available_macs |= available_macs
176221
fdb_entries.extend(fdb_entry)
177222

178-
db.close(db.ASIC_DB)
179-
180-
with open(filename, 'w') as fp:
181-
json.dump(fdb_entries, fp, indent=2, separators=(',', ': '))
182-
183-
return all_available_macs, map_mac_ip_per_vlan
223+
return fdb_entries, all_available_macs, map_mac_ip_per_vlan
184224

185225
def get_if(iff, cmd):
186226
s = socket.socket()

setup.py

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
'swsssdk>=2.0.1',
171171
'tabulate==0.8.2',
172172
'xmltodict==0.12.0',
173+
'deepdiff==5.2.3',
173174
],
174175
setup_requires= [
175176
'pytest-runner',
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"LAG_MEMBER_TABLE:PortChannel0001:Ethernet128": {
3+
"expireat": 1613562033.6011732,
4+
"ttl": -0.001,
5+
"type": "hash",
6+
"value": {
7+
"status": "enabled"
8+
}
9+
}
10+
}
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT:oid:0x3a000000001724": {
3+
"expireat": 1613562026.636173,
4+
"ttl": -0.001,
5+
"type": "hash",
6+
"value": {
7+
"SAI_BRIDGE_PORT_ATTR_ADMIN_STATE": "true",
8+
"SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE": "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW",
9+
"SAI_BRIDGE_PORT_ATTR_PORT_ID": "oid:0x20000000016ea",
10+
"SAI_BRIDGE_PORT_ATTR_TYPE": "SAI_BRIDGE_PORT_TYPE_PORT"
11+
}
12+
},
13+
"ASIC_STATE:SAI_OBJECT_TYPE_HOSTIF:oid:0xd000000000eea": {
14+
"expireat": 1613562026.59113,
15+
"ttl": -0.001,
16+
"type": "hash",
17+
"value": {
18+
"SAI_HOSTIF_ATTR_NAME": "Ethernet128",
19+
"SAI_HOSTIF_ATTR_OBJ_ID": "oid:0x1000000000ec1",
20+
"SAI_HOSTIF_ATTR_OPER_STATUS": "true",
21+
"SAI_HOSTIF_ATTR_TYPE": "SAI_HOSTIF_TYPE_NETDEV",
22+
"SAI_HOSTIF_ATTR_VLAN_TAG": "SAI_HOSTIF_VLAN_TAG_KEEP"
23+
}
24+
},
25+
"ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER:oid:0x1b0000000016ee": {
26+
"expireat": 1613562026.634684,
27+
"ttl": -0.001,
28+
"type": "hash",
29+
"value": {
30+
"SAI_LAG_MEMBER_ATTR_EGRESS_DISABLE": "false",
31+
"SAI_LAG_MEMBER_ATTR_INGRESS_DISABLE": "false",
32+
"SAI_LAG_MEMBER_ATTR_LAG_ID": "oid:0x20000000016ea",
33+
"SAI_LAG_MEMBER_ATTR_PORT_ID": "oid:0x1000000000ec1"
34+
}
35+
},
36+
"ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\"oid:0x260000000016f3\",\"mac\":\"52:54:00:5D:FC:B7\",\"switch_id\":\"oid:0x21000000000000\"}": {
37+
"expireat": 1613562026.593537,
38+
"ttl": -0.001,
39+
"type": "hash",
40+
"value": {
41+
"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID": "oid:0x3a000000001724",
42+
"SAI_FDB_ENTRY_ATTR_PACKET_ACTION": "SAI_PACKET_ACTION_FORWARD",
43+
"SAI_FDB_ENTRY_ATTR_TYPE": "SAI_FDB_ENTRY_TYPE_DYNAMIC"
44+
}
45+
},
46+
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x26000000000013": {
47+
"expireat": 1613562026.598609,
48+
"ttl": -0.001,
49+
"type": "hash",
50+
"value": {
51+
"NULL": "NULL"
52+
}
53+
},
54+
"ASIC_STATE:SAI_OBJECT_TYPE_VLAN:oid:0x260000000016f3": {
55+
"expireat": 1613562026.589369,
56+
"ttl": -0.001,
57+
"type": "hash",
58+
"value": {
59+
"SAI_VLAN_ATTR_VLAN_ID": "2"
60+
}
61+
}
62+
}

tests/fast_reboot_dump_test.py

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import json
2+
import os
3+
from deepdiff import DeepDiff
4+
from utilities_common.db import Db
5+
import importlib
6+
fast_reboot_dump = importlib.import_module("scripts.fast-reboot-dump")
7+
8+
class TestFastRebootDump(object):
9+
10+
@classmethod
11+
def setup_class(cls):
12+
print("SETUP")
13+
14+
test_db_dumps_directory = os.getcwd() + '/tests/fast_reboot_dump_dbs'
15+
asic_db_object = Db()
16+
app_db_object = Db()
17+
asic_db = asic_db_object.db
18+
app_db = app_db_object.db
19+
populate_db(asic_db, test_db_dumps_directory, 'ASIC_DB.json')
20+
populate_db(app_db, test_db_dumps_directory, 'APPL_DB.json')
21+
22+
cls.asic_db = asic_db
23+
cls.app_db = app_db
24+
25+
#Test fast-reboot-dump script to generate all required objects when there is a VLAN interface with a PortChannel member.
26+
def test_generate_fdb_entries_vlan_portcahnnel_member(self):
27+
vlan_ifaces = ['Vlan2']
28+
29+
fdb_entries, all_available_macs, map_mac_ip_per_vlan = fast_reboot_dump.generate_fdb_entries_logic(self.asic_db, self.app_db, vlan_ifaces)
30+
31+
expectd_fdb_entries = [{'FDB_TABLE:Vlan2:52-54-00-5D-FC-B7': {'type': 'dynamic', 'port': 'PortChannel0001'}, 'OP': 'SET'}]
32+
assert not DeepDiff(fdb_entries, expectd_fdb_entries, ignore_order=True)
33+
34+
expectd_all_available_macs = {('Vlan2', '52:54:00:5d:fc:b7')}
35+
assert not DeepDiff(all_available_macs, expectd_all_available_macs, ignore_order=True)
36+
37+
expectd_map_mac_ip_per_vlan = {'Vlan2': {'52:54:00:5d:fc:b7': 'PortChannel0001'}}
38+
assert not DeepDiff(map_mac_ip_per_vlan, expectd_map_mac_ip_per_vlan, ignore_order=True)
39+
40+
@classmethod
41+
def teardown_class(cls):
42+
print("TEARDOWN")
43+
44+
def populate_db(dbconn, test_db_dumps_directory, db_dump_filename):
45+
db = getattr(dbconn, db_dump_filename.replace('.json',''))
46+
with open(test_db_dumps_directory + '/' + db_dump_filename) as DB:
47+
db_dump = json.load(DB)
48+
for table, fields in db_dump.items():
49+
for key, value in fields['value'].items():
50+
dbconn.set(db, table, key, value)

0 commit comments

Comments
 (0)