Skip to content

Commit 44c5d1c

Browse files
vaibhavhdisabelmsft
authored andcommitted
[db_migrator] Fix migration of Loopback data: handle all Loopback interfaces (sonic-net#2560)
Fix the issue where cross branch upgrades (base DB version 1_0_1) lead to a OA crash due to a duplicate IP2ME route being added when there are more than one Loopback interfaces. The issue happens as in current implementation lo is hardcoded to be replaced as Loopback0. When the base image's APP DB has more than one IP assigned to lo interface, upon migration, all the IPs are assinged to same loopback Loopback0. This is incorrect, as in newer images different IPs are assinged to distinct Loopback interfaces. How to verify it Verified on a physical testbed that this change fixes the OA crash issue. Also added a unit test to catch this issue in PR tests.
1 parent ccefd45 commit 44c5d1c

6 files changed

+113
-13
lines changed

scripts/db_migrator.py

+30-13
Original file line numberDiff line numberDiff line change
@@ -178,22 +178,39 @@ def migrate_intf_table(self):
178178
if self.appDB is None:
179179
return
180180

181-
data = self.appDB.keys(self.appDB.APPL_DB, "INTF_TABLE:*")
182-
183-
if data is None:
181+
# Get Lo interface corresponding to IP(v4/v6) address from CONFIG_DB.
182+
configdb_data = self.configDB.get_keys('LOOPBACK_INTERFACE')
183+
lo_addr_to_int = dict()
184+
for int_data in configdb_data:
185+
if type(int_data) == tuple and len(int_data) > 1:
186+
intf_name = int_data[0]
187+
intf_addr = int_data[1]
188+
lo_addr_to_int.update({intf_addr: intf_name})
189+
190+
lo_data = self.appDB.keys(self.appDB.APPL_DB, "INTF_TABLE:*")
191+
if lo_data is None:
184192
return
185193

186194
if_db = []
187-
for key in data:
188-
if_name = key.split(":")[1]
189-
if if_name == "lo":
190-
self.appDB.delete(self.appDB.APPL_DB, key)
191-
key = key.replace(if_name, "Loopback0")
192-
log.log_info('Migrating lo entry to ' + key)
193-
self.appDB.set(self.appDB.APPL_DB, key, 'NULL', 'NULL')
194-
195-
if '/' not in key:
196-
if_db.append(key.split(":")[1])
195+
for lo_row in lo_data:
196+
# Example of lo_row: 'INTF_TABLE:lo:10.1.0.32/32'
197+
# Delete the old row with name as 'lo'. A new row with name as Loopback will be added
198+
lo_name_appdb = lo_row.split(":")[1]
199+
if lo_name_appdb == "lo":
200+
self.appDB.delete(self.appDB.APPL_DB, lo_row)
201+
lo_addr = lo_row.split('INTF_TABLE:lo:')[1]
202+
lo_name_configdb = lo_addr_to_int.get(lo_addr)
203+
if lo_name_configdb is None or lo_name_configdb == '':
204+
# an unlikely case where a Loopback address is present in APPLDB, but
205+
# there is no corresponding interface for this address in CONFIGDB:
206+
# Default to legacy implementation: hardcode interface name as Loopback0
207+
lo_new_row = lo_row.replace(lo_name_appdb, "Loopback0")
208+
else:
209+
lo_new_row = lo_row.replace(lo_name_appdb, lo_name_configdb)
210+
self.appDB.set(self.appDB.APPL_DB, lo_new_row, 'NULL', 'NULL')
211+
212+
if '/' not in lo_row:
213+
if_db.append(lo_row.split(":")[1])
197214
continue
198215

199216
data = self.appDB.keys(self.appDB.APPL_DB, "INTF_TABLE:*")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"INTF_TABLE:Loopback0" : {"NULL": "NULL"},
3+
"INTF_TABLE:Loopback0:10.1.0.32/32" : {"NULL": "NULL"},
4+
"INTF_TABLE:Loopback0:FC00:1::32/128" : {"NULL": "NULL"},
5+
"INTF_TABLE:Loopback1" : {"NULL": "NULL"},
6+
"INTF_TABLE:Loopback1:10.20.8.199/32" : {"NULL": "NULL"},
7+
"INTF_TABLE:Loopback1:2001:506:28:500::1/128" : {"NULL": "NULL"}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"INTF_TABLE:lo:10.1.0.32/32" : {
3+
"scope": "global",
4+
"family": "IPv4"
5+
},
6+
"INTF_TABLE:lo:FC00:1::32/128" : {
7+
"scope": "global",
8+
"family": "IPv6"
9+
},
10+
"INTF_TABLE:lo:10.20.8.199/32" : {
11+
"scope": "global",
12+
"family": "IPv4"
13+
},
14+
"INTF_TABLE:lo:2001:506:28:500::1/128" : {
15+
"scope": "global",
16+
"family": "IPv6"
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"LOOPBACK_INTERFACE|Loopback0" : {"NULL": "NULL"},
3+
"LOOPBACK_INTERFACE|Loopback0|10.1.0.32/32" : {"NULL": "NULL"},
4+
"LOOPBACK_INTERFACE|Loopback0|FC00:1::32/128" : {"NULL": "NULL"},
5+
"LOOPBACK_INTERFACE|Loopback1" : {"NULL": "NULL"},
6+
"LOOPBACK_INTERFACE|Loopback1|10.20.8.199/32" : {"NULL": "NULL"},
7+
"LOOPBACK_INTERFACE|Loopback1|2001:506:28:500::1/128" : {"NULL": "NULL"}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"LOOPBACK_INTERFACE|Loopback0|10.1.0.32/32" : {"NULL": "NULL"},
3+
"LOOPBACK_INTERFACE|Loopback0|FC00:1::32/128" : {"NULL": "NULL"},
4+
"LOOPBACK_INTERFACE|Loopback1|10.20.8.199/32" : {"NULL": "NULL"},
5+
"LOOPBACK_INTERFACE|Loopback1|2001:506:28:500::1/128" : {"NULL": "NULL"},
6+
"VERSIONS|DATABASE": {"VERSION": "version_1_0_1"}
7+
}

tests/db_migrator_test.py

+42
Original file line numberDiff line numberDiff line change
@@ -476,3 +476,45 @@ def test_warm_upgrade_to_2_0_2(self):
476476
expected_table = expected_db.cfgdb.get_table(table)
477477
diff = DeepDiff(resulting_table, expected_table, ignore_order=True)
478478
assert not diff
479+
480+
class Test_Migrate_Loopback(object):
481+
@classmethod
482+
def setup_class(cls):
483+
os.environ['UTILITIES_UNIT_TESTING'] = "2"
484+
485+
@classmethod
486+
def teardown_class(cls):
487+
os.environ['UTILITIES_UNIT_TESTING'] = "0"
488+
dbconnector.dedicated_dbs['CONFIG_DB'] = None
489+
dbconnector.dedicated_dbs['APPL_DB'] = None
490+
491+
def test_migrate_loopback_int(self):
492+
dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'config_db', 'loopback_interface_migrate_from_1_0_1_input')
493+
dbconnector.dedicated_dbs['APPL_DB'] = os.path.join(mock_db_path, 'appl_db', 'loopback_interface_migrate_from_1_0_1_input')
494+
495+
import db_migrator
496+
dbmgtr = db_migrator.DBMigrator(None)
497+
dbmgtr.migrate()
498+
dbconnector.dedicated_dbs['CONFIG_DB'] = os.path.join(mock_db_path, 'config_db', 'loopback_interface_migrate_from_1_0_1_expected')
499+
dbconnector.dedicated_dbs['APPL_DB'] = os.path.join(mock_db_path, 'appl_db', 'loopback_interface_migrate_from_1_0_1_expected')
500+
expected_db = Db()
501+
502+
# verify migrated configDB
503+
resulting_table = dbmgtr.configDB.get_table("LOOPBACK_INTERFACE")
504+
expected_table = expected_db.cfgdb.get_table("LOOPBACK_INTERFACE")
505+
diff = DeepDiff(resulting_table, expected_table, ignore_order=True)
506+
assert not diff
507+
508+
# verify migrated appDB
509+
expected_appl_db = SonicV2Connector(host='127.0.0.1')
510+
expected_appl_db.connect(expected_appl_db.APPL_DB)
511+
expected_keys = expected_appl_db.keys(expected_appl_db.APPL_DB, "INTF_TABLE:*")
512+
expected_keys.sort()
513+
resulting_keys = dbmgtr.appDB.keys(dbmgtr.appDB.APPL_DB, "INTF_TABLE:*")
514+
resulting_keys.sort()
515+
assert expected_keys == resulting_keys
516+
for key in expected_keys:
517+
resulting_keys = dbmgtr.appDB.get_all(dbmgtr.appDB.APPL_DB, key)
518+
expected_keys = expected_appl_db.get_all(expected_appl_db.APPL_DB, key)
519+
diff = DeepDiff(resulting_keys, expected_keys, ignore_order=True)
520+
assert not diff

0 commit comments

Comments
 (0)