Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[show platform summary] Add chassis hardware info to platform summary and version #1624

Merged
merged 13 commits into from
Jun 4, 2021
Merged
33 changes: 11 additions & 22 deletions show/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import utilities_common.cli as clicommon
import utilities_common.multi_asic as multi_asic_util
from natsort import natsorted
from sonic_py_common import device_info, multi_asic
from sonic_py_common import device_info
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
from tabulate import tabulate
from utilities_common import util_base
Expand Down Expand Up @@ -955,22 +955,9 @@ def logging(process, lines, follow, verbose):
def version(verbose):
"""Show version information"""
version_info = device_info.get_sonic_version_info()

platform = device_info.get_platform()
hwsku = device_info.get_hwsku()
asic_type = version_info['asic_type']
asic_count = multi_asic.get_num_asics()

serial_number = None
db = SonicV2Connector()
db.connect(db.STATE_DB)
eeprom_table = db.get_all(db.STATE_DB, 'EEPROM_INFO|0x23')
if "Name" in eeprom_table and eeprom_table["Name"] == "Serial Number" and "Value" in eeprom_table:
serial_number = eeprom_table["Value"]
else:
serial_number_cmd = "sudo decode-syseeprom -s"
serial_number = subprocess.Popen(serial_number_cmd, shell=True, text=True, stdout=subprocess.PIPE).stdout.read()

platform_info = device_info.get_platform_info()
chassis_info = platform.get_chassis_info()

sys_uptime_cmd = "uptime"
sys_uptime = subprocess.Popen(sys_uptime_cmd, shell=True, text=True, stdout=subprocess.PIPE)

Expand All @@ -980,11 +967,13 @@ def version(verbose):
click.echo("Build commit: {}".format(version_info['commit_id']))
click.echo("Build date: {}".format(version_info['build_date']))
click.echo("Built by: {}".format(version_info['built_by']))
click.echo("\nPlatform: {}".format(platform))
click.echo("HwSKU: {}".format(hwsku))
click.echo("ASIC: {}".format(asic_type))
click.echo("ASIC Count: {}".format(asic_count))
click.echo("Serial Number: {}".format(serial_number.strip()))
click.echo("\nPlatform: {}".format(platform_info['platform']))
click.echo("HwSKU: {}".format(platform_info['hwsku']))
click.echo("ASIC: {}".format(platform_info['asic_type']))
click.echo("ASIC Count: {}".format(platform_info['asic_count']))
click.echo("Serial Number: {}".format(chassis_info['serial']))
click.echo("Model Number: {}".format(chassis_info['model']))
click.echo("Hardware Revision: {}".format(chassis_info['revision']))
click.echo("Uptime: {}".format(sys_uptime.stdout.read().strip()))
click.echo("\nDocker images:")
cmd = 'sudo docker images --format "table {{.Repository}}\\t{{.Tag}}\\t{{.ID}}\\t{{.Size}}"'
Expand Down
49 changes: 30 additions & 19 deletions show/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,33 @@

import click
import utilities_common.cli as clicommon
from sonic_py_common import device_info, multi_asic
from sonic_py_common import device_info

#
# Helper functions
#

def get_hw_info_dict():
def get_chassis_info():
"""
This function is used to get the HW info helper function
Attempts to get the chassis info via STATE_DB and falls back to direct Platform API calls.
"""
hw_info_dict = {}

version_info = device_info.get_sonic_version_info()
chassis_info = device_info.get_chassis_info()
required_keys = ['serial', 'model', 'revision']
failed_vals = ['', 'N/A']
platform_chassis = None

hw_info_dict['platform'] = device_info.get_platform()
hw_info_dict['hwsku'] = device_info.get_hwsku()
hw_info_dict['asic_type'] = version_info['asic_type']
hw_info_dict['asic_count'] = multi_asic.get_num_asics()

return hw_info_dict
for k in required_keys:
if chassis_info.get(k, '') in failed_vals:
if platform_chassis is None:
import platform
platform_chassis = sonic_platform.platform.Platform().get_chassis()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This get_chassis() trying to call Eeprom() to retrieve EEPROM details but failed due to permission error to access Eeprom details via sysfs by a non-root user for many platforms.

We can suppress this error by adding a root user check in Eeprom() to return "N/A" instead of throwing a permission error exception when non-root user trying to access Eeprom() API.
But consequence is that until PMON comes up to fill the DB with EEPROM details, show version will display serial number, hwsku, etc as "None" since Eeprom() is not accessible by non-root users.

This consequence is a day 1 behavior, but not seen earlier because previously we retrieve the Eeprom details by deliberately calling "decode-syseeprom" with sudo privileges even in non-root user environment in code.

try:
chassis_info[k] = getattr(platform_chassis, "get_".format(k))()
except AttributeError:
chassis_info[k] = 'N/A'

return chassis_info

#
# 'platform' group ("show platform ...")
Expand All @@ -38,17 +47,19 @@ def platform():
@click.option('--json', is_flag=True, help="Output in JSON format")
def summary(json):
"""Show hardware platform information"""

hw_info_dict = {}
hw_info_dict = get_hw_info_dict()
platform_info = device_info.get_platform_info()
chassis_info = get_chassis_info()

if json:
click.echo(clicommon.json_dump(hw_info_dict))
click.echo(clicommon.json_dump({**platform_info, **chassis_info}))
else:
click.echo("Platform: {}".format(hw_info_dict['platform']))
click.echo("HwSKU: {}".format(hw_info_dict['hwsku']))
click.echo("ASIC: {}".format(hw_info_dict['asic_type']))
click.echo("ASIC Count: {}".format(hw_info_dict['asic_count']))
click.echo("Platform: {}".format(platform_info['platform']))
click.echo("HwSKU: {}".format(platform_info['hwsku']))
click.echo("ASIC: {}".format(platform_info['asic_type']))
click.echo("ASIC Count: {}".format(platform_info['asic_count']))
click.echo("Serial Number: {}".format(chassis_info['serial']))
click.echo("Model Number: {}".format(chassis_info['model']))
click.echo("Hardware Revision: {}".format(chassis_info['revision']))


# 'syseeprom' subcommand ("show platform syseeprom")
Expand Down
22 changes: 16 additions & 6 deletions tests/show_platform_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,29 @@ class TestShowPlatform(object):
TEST_PLATFORM = "x86_64-mlnx_msn2700-r0"
TEST_HWSKU = "Mellanox-SN2700"
TEST_ASIC_TYPE = "mellanox"
TEST_ASIC_COUNT = 1
TEST_SERIAL = "MT1822K07815"
TEST_MODEL = "MSN2700-CS2FO"
TEST_REV = "A1"

# Test 'show platform summary'
def test_summary(self):
expected_output = """\
Platform: {}
HwSKU: {}
ASIC: {}
""".format(self.TEST_PLATFORM, self.TEST_HWSKU, self.TEST_ASIC_TYPE)

with mock.patch("show.platform.get_hw_info_dict",
return_value={"platform": self.TEST_PLATFORM, "hwsku": self.TEST_HWSKU, "asic_type": self.TEST_ASIC_TYPE}):
result = CliRunner().invoke(show.cli.commands["platform"].commands["summary"], [])
assert result.output == textwrap.dedent(expected_output)
ASIC Count: {}
Serial Number: {}
Model Number: {}
Hardware Revision: {}
""".format(self.TEST_PLATFORM, self.TEST_HWSKU, self.TEST_ASIC_TYPE, self.TEST_ASIC_COUNT, self.TEST_SERIAL, self.TEST_MODEL, self.TEST_REV)

with mock.patch("sonic_py_common.device_info.get_platform_info",
return_value={"platform": self.TEST_PLATFORM, "hwsku": self.TEST_HWSKU, "asic_type": self.TEST_ASIC_TYPE, "asic_count": self.TEST_ASIC_COUNT}):
with mock.patch("show.platform.get_chassis_info",
return_value={"serial": self.TEST_SERIAL, "model": self.TEST_MODEL, "revision": self.TEST_REV}):
result = CliRunner().invoke(show.cli.commands["platform"].commands["summary"], [])
assert result.output == textwrap.dedent(expected_output)


class TestShowPlatformPsu(object):
Expand Down