Skip to content

Commit de7981d

Browse files
SuvarnaMeenakshiVladimirKuk
authored andcommitted
Add script to periodically update oper status of management interface (sonic-net#21245)
Issue to be fix: Currently operational status of mgmt interface is not present or correct for multi-asic devices. Root cause: Operational status of mgmt interface is updated by portsyncd in swss docker. In case of multi-asic platform, swss service is started only in asic namespace context. Since portsyncd is running in a specific network namespace context, it is not aware of mgmt interface present in the host namespace of multi-asic platform. Therefore there is no way for portsyncd to find the operational status of mgmt interface and update in STATE_DB MGMT_PORT_TABLE. Use case: SNMP interface MIB reads MGMT_PORT_TABLE in STATE_DB to retrieve oper status of mgmt interface periodically. In case of multi-asic platform, currently this is returning the oper status of 'eth0' interface which is the virtual interface that is present inside asic namespace which gets created as a part of database docker and is not the actual management interface. --------- Signed-off-by: Suvarna Meenakshi <[email protected]>
1 parent 2e47cfd commit de7981d

File tree

4 files changed

+130
-0
lines changed

4 files changed

+130
-0
lines changed

files/build_templates/sonic_debian_extension.j2

+2
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,8 @@ sudo cp $IMAGE_CONFIGS/monit/arp_update_checker $FILESYSTEM_ROOT/usr/bin/
402402
sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/arp_update_checker
403403
sudo cp $IMAGE_CONFIGS/monit/control_plane_drop_check $FILESYSTEM_ROOT/usr/bin/
404404
sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/control_plane_drop_check
405+
sudo cp $IMAGE_CONFIGS/monit/mgmt_oper_status.py $FILESYSTEM_ROOT/usr/bin/
406+
sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/mgmt_oper_status.py
405407

406408
# Installed smartmontools version should match installed smartmontools in docker-platform-monitor Dockerfile
407409
# TODO: are mismatching versions fine for bookworm?

files/image_config/monit/conf.d/sonic-host

+5
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,8 @@ check program arp_update_checker with path "/usr/bin/arp_update_checker" every 1
6565
check program controlPlaneDropCheck with path "/usr/bin/control_plane_drop_check"
6666
every 5 cycles
6767
if status != 0 for 3 cycle then alert repeat every 1 cycles
68+
69+
# Periodically update oper status of mgmt interface in STATE_DB
70+
check program mgmtOperStatus with path "/usr/bin/mgmt_oper_status.py"
71+
every 1 cycles
72+
if status != 0 for 3 cycle then alert repeat every 1 cycles
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python3
2+
3+
"""
4+
"""
5+
import sys
6+
import subprocess
7+
import syslog
8+
9+
from sonic_py_common import multi_asic, device_info
10+
from swsscommon.swsscommon import SonicV2Connector
11+
12+
13+
def main():
14+
db = SonicV2Connector(use_unix_socket_path=True)
15+
db.connect('CONFIG_DB')
16+
db.connect('STATE_DB')
17+
mgmt_ports_keys = db.keys(db.CONFIG_DB, 'MGMT_PORT|*' )
18+
if not mgmt_ports_keys:
19+
syslog.syslog(syslog.LOG_DEBUG, 'No management interface found')
20+
else:
21+
try:
22+
mgmt_ports = [key.split('MGMT_PORT|')[-1] for key in mgmt_ports_keys]
23+
for port in mgmt_ports:
24+
state_db_mgmt_port = db.keys(db.STATE_DB, 'MGMT_PORT_TABLE|*' )
25+
state_db_key = "MGMT_PORT_TABLE|{}".format(port)
26+
prev_oper_status = 'unknown'
27+
if state_db_key in state_db_mgmt_port:
28+
prev_oper_status = db.get(db.STATE_DB, state_db_key, 'oper_status')
29+
port_operstate_path = '/sys/class/net/{}/operstate'.format(port)
30+
oper_status = subprocess.run(['cat', port_operstate_path], capture_output=True, text=True)
31+
current_oper_status = oper_status.stdout.strip()
32+
if current_oper_status != prev_oper_status:
33+
db.set(db.STATE_DB, state_db_key, 'oper_status', current_oper_status)
34+
log_level = syslog.LOG_INFO if current_oper_status == 'up' else syslog.LOG_WARNING
35+
syslog.syslog(log_level, "mgmt_oper_status: {}".format(current_oper_status))
36+
except Exception as e:
37+
syslog.syslog(syslog.LOG_ERR, "mgmt_oper_status exception : {}".format(str(e)))
38+
db.set(db.STATE_DB, state_db_key, 'oper_status', 'unknown')
39+
sys.exit(1)
40+
41+
42+
if __name__ == "__main__":
43+
main()
44+
sys.exit(0)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import unittest
2+
from unittest.mock import patch, MagicMock
3+
import subprocess
4+
import syslog
5+
import sys
6+
import mgmt_oper_status
7+
8+
class TestMgmtOperStatusCheck(unittest.TestCase):
9+
10+
@patch('mgmt_oper_status.SonicV2Connector')
11+
@patch('mgmt_oper_status.subprocess.run')
12+
@patch('mgmt_oper_status.syslog.syslog')
13+
def test_main_no_mgmt_ports(self, mock_syslog, mock_subprocess, mock_SonicV2Connector):
14+
mock_db = MagicMock()
15+
mock_SonicV2Connector.return_value = mock_db
16+
mock_db.keys.return_value = []
17+
18+
mgmt_oper_status.main()
19+
20+
mock_syslog.assert_called_with(syslog.LOG_DEBUG, 'No management interface found')
21+
22+
@patch('mgmt_oper_status.SonicV2Connector')
23+
@patch('mgmt_oper_status.subprocess.run')
24+
@patch('mgmt_oper_status.syslog.syslog')
25+
def test_main_with_mgmt_ports(self, mock_syslog, mock_subprocess, mock_SonicV2Connector):
26+
mock_db = MagicMock()
27+
mock_SonicV2Connector.return_value = mock_db
28+
mgmt_ports_keys = ['MGMT_PORT|eth0', 'MGMT_PORT|eth1']
29+
mock_db.keys.return_value = mgmt_ports_keys
30+
mock_db.set.return_value = None
31+
32+
mock_subprocess.return_value = subprocess.CompletedProcess(args=['cat', '/sys/class/net/eth0/operstate'], returncode=0, stdout='up', stderr='')
33+
34+
mgmt_oper_status.main()
35+
36+
mock_syslog.assert_any_call(syslog.LOG_INFO, 'mgmt_oper_status: up')
37+
mock_syslog.assert_any_call(syslog.LOG_INFO, 'mgmt_oper_status: up')
38+
39+
mock_db.set.assert_any_call(mock_db.STATE_DB, 'MGMT_PORT_TABLE|eth0', 'oper_status', 'up')
40+
mock_db.set.assert_any_call(mock_db.STATE_DB, 'MGMT_PORT_TABLE|eth1', 'oper_status', 'up')
41+
42+
@patch('mgmt_oper_status.SonicV2Connector')
43+
@patch('mgmt_oper_status.subprocess.run')
44+
@patch('mgmt_oper_status.syslog.syslog')
45+
def test_main_with_mgmt_port_down(self, mock_syslog, mock_subprocess, mock_SonicV2Connector):
46+
mock_db = MagicMock()
47+
mock_SonicV2Connector.return_value = mock_db
48+
mgmt_ports_keys = ['MGMT_PORT|eth0']
49+
mock_db.keys.return_value = mgmt_ports_keys
50+
mock_db.set.return_value = None
51+
52+
mock_subprocess.return_value = subprocess.CompletedProcess(args=['cat', '/sys/class/net/eth0/operstate'], returncode=0, stdout='down', stderr='')
53+
54+
mgmt_oper_status.main()
55+
56+
mock_syslog.assert_any_call(syslog.LOG_WARNING, 'mgmt_oper_status: down')
57+
58+
mock_db.set.assert_any_call(mock_db.STATE_DB, 'MGMT_PORT_TABLE|eth0', 'oper_status', 'down')
59+
60+
61+
@patch('mgmt_oper_status.SonicV2Connector')
62+
@patch('mgmt_oper_status.subprocess.run')
63+
@patch('mgmt_oper_status.syslog.syslog')
64+
def test_main_exception_handling(self, mock_syslog, mock_subprocess, mock_SonicV2Connector):
65+
mock_db = MagicMock()
66+
mock_SonicV2Connector.return_value = mock_db
67+
mgmt_ports_keys = ['MGMT_PORT|eth0']
68+
mock_db.keys.return_value = mgmt_ports_keys
69+
mock_db.set.return_value = None
70+
71+
mock_subprocess.side_effect = Exception("File not found")
72+
73+
mgmt_oper_status.main()
74+
75+
mock_syslog.assert_called_with(syslog.LOG_ERR, "mgmt_oper_status exception : File not found")
76+
mock_db.set.assert_any_call(mock_db.STATE_DB, 'MGMT_PORT_TABLE|eth0', 'oper_status', 'unknown')
77+
78+
if __name__ == '__main__':
79+
unittest.main()

0 commit comments

Comments
 (0)