Skip to content

Commit

Permalink
[sonic_py_common] Cache Static Information in device_info to speed up…
Browse files Browse the repository at this point in the history
… CLI response (sonic-net#11696)

- Why I did it
Profiled the execution for the following cmd intfutil -c status

- How I did it
Cached the following information:
1. get_sonic_version_info()
2. get_platform_info()
None of the API exposed to the user libraries (for eg: sonic-utilities) has been modified
These methods involve reading text files or from redis. Thus, caching helped to improve the execution time

- How to verify it
Added UT's.
Verified on the device

Signed-off-by: Vivek Reddy Karri <vkarri@nvidia.com>
  • Loading branch information
vivekrnv committed Aug 29, 2022
1 parent 0011771 commit ee05ec8
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 6 deletions.
22 changes: 16 additions & 6 deletions src/sonic-py-common/sonic_py_common/device_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
CHASSIS_INFO_MODEL_FIELD = 'model'
CHASSIS_INFO_REV_FIELD = 'revision'

# Cacheable Objects
sonic_ver_info = {}
hw_info_dict = {}

def get_localhost_info(field, config_db=None):
try:
# TODO: enforce caller to provide config_db explicitly and remove its default value
Expand Down Expand Up @@ -334,14 +338,17 @@ def get_sonic_version_info():
if not os.path.isfile(SONIC_VERSION_YAML_PATH):
return None

data = {}
global sonic_ver_info
if sonic_ver_info:
return sonic_ver_info

with open(SONIC_VERSION_YAML_PATH) as stream:
if yaml.__version__ >= "5.1":
data = yaml.full_load(stream)
sonic_ver_info = yaml.full_load(stream)
else:
data = yaml.load(stream)
sonic_ver_info = yaml.load(stream)

return data
return sonic_ver_info

def get_sonic_version_file():
if not os.path.isfile(SONIC_VERSION_YAML_PATH):
Expand All @@ -355,9 +362,12 @@ def get_platform_info(config_db=None):
"""
This function is used to get the HW info helper function
"""
from .multi_asic import get_num_asics
global hw_info_dict

hw_info_dict = {}
if hw_info_dict:
return hw_info_dict

from .multi_asic import get_num_asics

version_info = get_sonic_version_info()

Expand Down
79 changes: 79 additions & 0 deletions src/sonic-py-common/tests/device_info_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,32 @@
'onie_kernel_version': '4.10.11'
}

SONIC_VERISON_YML = """\
---
build_version: 'test_branch.1-a8fbac59d'
debian_version: '11.4'
kernel_version: '5.10.0-12-2-amd64'
asic_type: mellanox
asic_subtype: 'mellanox'
commit_id: 'a8fbac59d'
branch: 'test_branch'
release: 'master'
libswsscommon: 1.0.0
sonic_utilities: 1.2"""

SONIC_VERISON_YML_RESULT = {
'build_version': 'test_branch.1-a8fbac59d',
'debian_version': '11.4',
'kernel_version': '5.10.0-12-2-amd64',
'asic_type': 'mellanox',
'asic_subtype': 'mellanox',
'commit_id': 'a8fbac59d',
'branch': 'test_branch',
'release': 'master',
'libswsscommon': '1.0.0',
'sonic_utilities': 1.2
}

class TestDeviceInfo(object):
@pytest.fixture(scope="class", autouse=True)
def sanitize_environment(self):
Expand Down Expand Up @@ -83,6 +109,59 @@ def test_get_chassis_info(self):
"revision": SonicV2Connector.TEST_REV}
assert result == truth

@mock.patch("os.path.isfile")
def test_get_sonic_version(self, mock_isfile):
mock_isfile.return_value = True
open_mocked = mock.mock_open(read_data=SONIC_VERISON_YML)
with mock.patch("{}.open".format(BUILTINS), open_mocked):
for _ in range(0,5):
assert device_info.get_sonic_version_info() == SONIC_VERISON_YML_RESULT
# Assert the file was read only once
open_mocked.assert_called_once_with(device_info.SONIC_VERSION_YAML_PATH)

@mock.patch("sonic_py_common.device_info.get_platform_info")
def test_is_chassis(self, mock_platform_info):
mock_platform_info.return_value = {"switch_type": "npu"}
assert device_info.is_chassis() == False
assert device_info.is_voq_chassis() == False
assert device_info.is_packet_chassis() == False

mock_platform_info.return_value = {"switch_type": "voq"}
assert device_info.is_voq_chassis() == True
assert device_info.is_packet_chassis() == False
assert device_info.is_chassis() == True

mock_platform_info.return_value = {"switch_type": "chassis-packet"}
assert device_info.is_voq_chassis() == False
assert device_info.is_packet_chassis() == True
assert device_info.is_chassis() == True

mock_platform_info.return_value = {}
assert device_info.is_voq_chassis() == False
assert device_info.is_packet_chassis() == False
assert device_info.is_chassis() == False

@mock.patch("sonic_py_common.device_info.ConfigDBConnector", autospec=True)
@mock.patch("sonic_py_common.device_info.get_sonic_version_info")
@mock.patch("sonic_py_common.device_info.get_machine_info")
@mock.patch("sonic_py_common.device_info.get_hwsku")
def test_get_platform_info(self, mock_hwsku, mock_machine_info, mock_sonic_ver, mock_cfg_db):
mock_cfg_inst = mock_cfg_db.return_value
mock_cfg_inst.get_table.return_value = {"localhost": {"switch_type": "npu"}}
mock_sonic_ver.return_value = SONIC_VERISON_YML_RESULT
mock_machine_info.return_value = {"onie_platform" : "x86_64-mlnx_msn2700-r0"}
mock_hwsku.return_value = "Mellanox-SN2700"
for _ in range(0,5):
hw_info_dict = device_info.get_platform_info()
assert hw_info_dict["asic_type"] == "mellanox"
assert hw_info_dict["platform"] == "x86_64-mlnx_msn2700-r0"
assert hw_info_dict["hwsku"] == "Mellanox-SN2700"
assert hw_info_dict["switch_type"] == "npu"
assert mock_sonic_ver.called_once()
assert mock_machine_info.called_once()
assert mock_hwsku.called_once()
mock_cfg_inst.get_table.assert_called_once_with("DEVICE_METADATA")

@classmethod
def teardown_class(cls):
print("TEARDOWN")

0 comments on commit ee05ec8

Please sign in to comment.