Skip to content

Commit 7381005

Browse files
authored
Add script to periodically update oper status of management interface
Why I did it Cherry-pick of changes in sonic-net/sonic-buildimage#21245 Work item tracking Microsoft ADO 30279044: How I did it Remove update of mgmt oper status from sonic-swss Use monit script to periodically update oper status of mgmt interface. How to verify it Verified on Chassis platform
2 parents 93d97d0 + ce17907 commit 7381005

File tree

5 files changed

+130
-0
lines changed

5 files changed

+130
-0
lines changed

files/build_templates/sonic_debian_extension.j2

+2
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,8 @@ sudo cp $IMAGE_CONFIGS/monit/memory_checker $FILESYSTEM_ROOT/usr/bin/
393393
sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/memory_checker
394394
sudo cp $IMAGE_CONFIGS/monit/restart_service $FILESYSTEM_ROOT/usr/bin/
395395
sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/restart_service
396+
sudo cp $IMAGE_CONFIGS/monit/mgmt_oper_status.py $FILESYSTEM_ROOT/usr/bin/
397+
sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/mgmt_oper_status.py
396398

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

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

+5
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,8 @@ check program vnetRouteCheck with path "/usr/local/bin/vnet_route_check.py"
5656
# memory_check tool that verifies that memory usage does not cross the threshold or invokes techsupport.
5757
check program memory_check with path "/usr/local/bin/memory_threshold_check.py"
5858
if status == 2 for 10 times within 20 cycles then exec "/usr/local/bin/memory_threshold_check_handler.py"
59+
60+
# Periodically update oper status of mgmt interface in STATE_DB
61+
check program mgmtOperStatus with path "/usr/bin/mgmt_oper_status.py"
62+
every 1 cycles
63+
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)

files/image_config/monit/tests/__init__.py

Whitespace-only changes.
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)