From d70be9a21e12b111b6be67fabb3b12f3a765e33f Mon Sep 17 00:00:00 2001 From: Suvarna Meenakshi Date: Thu, 8 Apr 2021 03:02:20 +0000 Subject: [PATCH 1/2] [RFC1213][multi-asic]: Retrieve oper status of management interface from kernel for multi-asic platform. In multi-asic platform there is no daemon in the host namespace to update the oper status of management interface. Instead of retrieving this information from state_db, retrieve this from /sys/class/net/../operstate file. Signed-off-by: Suvarna Meenakshi --- src/sonic_ax_impl/mibs/ietf/rfc1213.py | 28 ++++++- tests/mock_tables/eth1 | 1 + tests/namespace/test_interfaces.py | 100 +++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tests/mock_tables/eth1 diff --git a/src/sonic_ax_impl/mibs/ietf/rfc1213.py b/src/sonic_ax_impl/mibs/ietf/rfc1213.py index 75238b054..20255fe12 100644 --- a/src/sonic_ax_impl/mibs/ietf/rfc1213.py +++ b/src/sonic_ax_impl/mibs/ietf/rfc1213.py @@ -3,6 +3,7 @@ import socket from enum import unique, Enum from bisect import bisect_right +import os from sonic_ax_impl import mibs from sonic_ax_impl.mibs import Namespace @@ -184,6 +185,7 @@ class IpMib(metaclass=MIBMeta, prefix='.1.3.6.1.2.1.4'): class InterfacesUpdater(MIBUpdater): RFC1213_MAX_SPEED = 4294967295 + if_operstate_file = "/sys/class/net/{}/operstate" def __init__(self): super().__init__() @@ -359,6 +361,21 @@ def _get_if_entry(self, sub_id): return Namespace.dbs_get_all(self.db_conn, db, if_table, blocking=True) + + def _get_mgmt_oper_status(self, if_name): + """ + :param if_name: mgmt interface name + :return: operation status string + """ + status = "unknown" + if_operstate_file = self.if_operstate_file.format(if_name) + if os.path.exists(if_operstate_file): + return "up" + file = open(if_operstate_file, "r") + if file is not None: + status = file.readline().strip() + return status + def _get_if_entry_state_db(self, sub_id): """ :param oid: The 1-based sub-identifier query. @@ -372,7 +389,16 @@ def _get_if_entry_state_db(self, sub_id): db = mibs.STATE_DB if oid in self.mgmt_oid_name_map: mgmt_if_name = self.mgmt_oid_name_map[oid] - if_table = mibs.mgmt_if_entry_table_state_db(mgmt_if_name) + """ + For multi-asic platform, operstatus of + management iface is not stored in state db, + Retrieve oper status from sys/class/net + """ + if len(self.db_conn) > 1: + state = self._get_mgmt_oper_status(mgmt_if_name) + return {"oper_status":state} + else: + if_table = mibs.mgmt_if_entry_table_state_db(mgmt_if_name) else: return None diff --git a/tests/mock_tables/eth1 b/tests/mock_tables/eth1 new file mode 100644 index 000000000..e31ee94e1 --- /dev/null +++ b/tests/mock_tables/eth1 @@ -0,0 +1 @@ +up diff --git a/tests/namespace/test_interfaces.py b/tests/namespace/test_interfaces.py index ea51339f8..5b033594f 100644 --- a/tests/namespace/test_interfaces.py +++ b/tests/namespace/test_interfaces.py @@ -1,12 +1,14 @@ import os import sys import importlib +import mock # noinspection PyUnresolvedReferences import tests.mock_tables.dbconnector modules_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.insert(0, os.path.join(modules_path, 'src')) +mgmt_iface_status = modules_path + "/mock_tables/{}" from unittest import TestCase @@ -18,7 +20,10 @@ from ax_interface.mib import MIBTable from sonic_ax_impl.mibs.ietf import rfc1213 from sonic_ax_impl import mibs +import tests.mock_tables.mock_mgmt_oper_status +from mock import patch +@patch('sonic_ax_impl.mibs.ietf.rfc1213.InterfacesUpdater.if_operstate_file', mgmt_iface_status) class TestGetNextPDU(TestCase): @classmethod def setUpClass(cls): @@ -236,6 +241,101 @@ def test_if_type_portchannel(self): self.assertEqual(str(value0.name), str(ObjectIdentifier(11, 0, 1, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 3, 1001)))) self.assertEqual(value0.data, 161) + def test_mgmt_iface(self): + """ + Test that mgmt port is present in the MIB + """ + oid = ObjectIdentifier(11, 0, 0, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 1, 10000)) + get_pdu = GetNextPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(ObjectIdentifier(11, 0, 1, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 1, 10001)))) + self.assertEqual(value0.data, 10000) + + def test_mgmt_iface_description(self): + """ + Test mgmt port description (which is simply an alias) + """ + oid = ObjectIdentifier(11, 0, 0, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 2, 10001)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.OCTET_STRING) + self.assertEqual(str(value0.name), str(ObjectIdentifier(11, 0, 1, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 2, 10001)))) + self.assertEqual(str(value0.data), 'mgmt1') + + def test_mgmt_iface_oper_status(self): + """ + Test mgmt port operative status + """ + oid = ObjectIdentifier(11, 0, 0, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 8, 10001)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(ObjectIdentifier(11, 0, 1, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 8, 10001)))) + self.assertEqual(value0.data, 1) + + def test_mgmt_iface_oper_status_unknown(self): + """ + Test mgmt port operative status + """ + oid = ObjectIdentifier(11, 0, 0, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 8, 10002)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(ObjectIdentifier(11, 0, 1, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 8, 10002)))) + self.assertEqual(value0.data, 4) + + def test_mgmt_iface_admin_status(self): + """ + Test mgmt port admin status + """ + oid = ObjectIdentifier(11, 0, 0, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 7, 10001)) + get_pdu = GetPDU( + header=PDUHeader(1, PduTypes.GET, 16, 0, 42, 0, 0, 0), + oids=[oid] + ) + + encoded = get_pdu.encode() + response = get_pdu.make_response(self.lut) + print(response) + + value0 = response.values[0] + self.assertEqual(value0.type_, ValueType.INTEGER) + self.assertEqual(str(value0.name), str(ObjectIdentifier(11, 0, 1, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 7, 10001)))) + self.assertEqual(value0.data, 1) + def test_getnextpdu_first_bp_ifindex(self): oid = ObjectIdentifier(11, 0, 0, 0, (1, 3, 6, 1, 2, 1, 2, 2, 1, 1,1004)) get_pdu = GetNextPDU( From 98f8c7c30928c79b0168f20098cdf99ad27b9320 Mon Sep 17 00:00:00 2001 From: Suvarna Meenakshi Date: Thu, 8 Apr 2021 19:11:09 +0000 Subject: [PATCH 2/2] Update mgmt interface oper status periodically, instead of retrieving it during SNMP get to ensure SNMP get time does not increase. Signed-off-by: Suvarna Meenakshi --- src/sonic_ax_impl/mibs/ietf/rfc1213.py | 17 ++++++++++++++--- tests/namespace/test_interfaces.py | 11 +++++------ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/sonic_ax_impl/mibs/ietf/rfc1213.py b/src/sonic_ax_impl/mibs/ietf/rfc1213.py index 20255fe12..baa609d76 100644 --- a/src/sonic_ax_impl/mibs/ietf/rfc1213.py +++ b/src/sonic_ax_impl/mibs/ietf/rfc1213.py @@ -205,6 +205,7 @@ def __init__(self): self.if_id_map = {} self.oid_name_map = {} self.namespace_db_map = Namespace.get_namespace_db_map(self.db_conn) + self.mgmt_oper_status = {} def reinit_data(self): """ @@ -241,6 +242,14 @@ def update_data(self): list(self.oid_lag_name_map.keys()) + list(self.mgmt_oid_name_map.keys())) self.if_range = [(i,) for i in self.if_range] + """ + For multi-asic platform, operstatus of + management iface is not stored in state db, + Retrieve oper status from sys/class/net + """ + if len(self.db_conn) > 1: + for mgmt_oid,if_name in self.mgmt_oid_name_map.items(): + self.mgmt_oper_status[mgmt_oid] = self._get_mgmt_oper_status(if_name) def get_next(self, sub_id): """ @@ -370,7 +379,6 @@ def _get_mgmt_oper_status(self, if_name): status = "unknown" if_operstate_file = self.if_operstate_file.format(if_name) if os.path.exists(if_operstate_file): - return "up" file = open(if_operstate_file, "r") if file is not None: status = file.readline().strip() @@ -395,8 +403,11 @@ def _get_if_entry_state_db(self, sub_id): Retrieve oper status from sys/class/net """ if len(self.db_conn) > 1: - state = self._get_mgmt_oper_status(mgmt_if_name) - return {"oper_status":state} + if oid in self.mgmt_oper_status: + state = self.mgmt_oper_status[oid] + return {"oper_status":state} + else: + return None else: if_table = mibs.mgmt_if_entry_table_state_db(mgmt_if_name) else: diff --git a/tests/namespace/test_interfaces.py b/tests/namespace/test_interfaces.py index 5b033594f..2583ff9f7 100644 --- a/tests/namespace/test_interfaces.py +++ b/tests/namespace/test_interfaces.py @@ -20,20 +20,19 @@ from ax_interface.mib import MIBTable from sonic_ax_impl.mibs.ietf import rfc1213 from sonic_ax_impl import mibs -import tests.mock_tables.mock_mgmt_oper_status from mock import patch -@patch('sonic_ax_impl.mibs.ietf.rfc1213.InterfacesUpdater.if_operstate_file', mgmt_iface_status) class TestGetNextPDU(TestCase): @classmethod def setUpClass(cls): tests.mock_tables.dbconnector.load_namespace_config() importlib.reload(rfc1213) cls.lut = MIBTable(rfc1213.InterfacesMIB) - for updater in cls.lut.updater_instances: - updater.update_data() - updater.reinit_data() - updater.update_data() + with mock.patch('sonic_ax_impl.mibs.ietf.rfc1213.InterfacesUpdater.if_operstate_file', mgmt_iface_status): + for updater in cls.lut.updater_instances: + updater.update_data() + updater.reinit_data() + updater.update_data() def test_getnextpdu_noneifindex(self): # oid.include = 1