Skip to content

Commit d64a90a

Browse files
authored
Adds logic to get default disk and check disk type (sonic-net#3399)
* Added function to check if disk is SATA * Fixed eval bug unconvered by UT * Changed logger to syslogger; fixed UT failures * Added checks for partitions and filtered_disks; fixed static analysis issues * Fix static analysis errors E303, E711 * Changed information delivery semantics per review comment * Reverted syslogger to logger to maintain backward compatibility The syslogger-to-logger change is unrelated to the sonic-utilities change and breaks backwards compatibility. It should be a separate commit once SysLogger is in all the older versions. * Changed Disk 'type' --> 'Type' for uniformity * Made the fields look uniform * Disk Type Support for eMMC devices * Removed old sonic_ssd import
1 parent b2b9734 commit d64a90a

File tree

5 files changed

+188
-9
lines changed

5 files changed

+188
-9
lines changed

ssdutil/main.py

+54-9
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,61 @@
66
#
77

88
try:
9-
import argparse
109
import os
1110
import sys
11+
import argparse
12+
import psutil
13+
from blkinfo import BlkDiskInfo
1214

1315
from sonic_py_common import device_info, logger
1416
except ImportError as e:
1517
raise ImportError("%s - required module not found" % str(e))
1618

17-
DEFAULT_DEVICE="/dev/sda"
19+
DEFAULT_DEVICE = "/dev/sda"
1820
SYSLOG_IDENTIFIER = "ssdutil"
21+
DISK_TYPE_SSD = "sata"
1922

2023
# Global logger instance
2124
log = logger.Logger(SYSLOG_IDENTIFIER)
2225

2326

27+
def get_default_disk():
28+
"""Check default disk"""
29+
default_device = DEFAULT_DEVICE
30+
host_mnt = '/host'
31+
host_partition = None
32+
partitions = psutil.disk_partitions()
33+
34+
if partitions is None:
35+
return (default_device, None)
36+
37+
for parts in partitions:
38+
if parts.mountpoint == host_mnt:
39+
host_partition = parts
40+
break
41+
42+
disk_major = os.major(os.stat(host_partition.device).st_rdev)
43+
filters = {
44+
'maj:min': '{}:0'.format(disk_major)
45+
}
46+
47+
myblkd = BlkDiskInfo()
48+
my_filtered_disks = myblkd.get_disks(filters)
49+
50+
if my_filtered_disks is None:
51+
return (default_device, None)
52+
53+
json_output = my_filtered_disks[0]
54+
blkdev = json_output['name']
55+
disk_type = json_output['tran']
56+
default_device = os.path.join("/dev/", blkdev)
57+
58+
# Disk Type Support for eMMC devices
59+
disk_type = 'eMMC' if len(disk_type) == 0 and 'mmcblk' in host_partition.device else disk_type # noqa: E501
60+
61+
return default_device, disk_type
62+
63+
2464
def import_ssd_api(diskdev):
2565
"""
2666
Loads platform specific or generic ssd_util module from source
@@ -37,44 +77,49 @@ def import_ssd_api(diskdev):
3777
sys.path.append(os.path.abspath(platform_plugins_path))
3878
from ssd_util import SsdUtil
3979
except ImportError as e:
40-
log.log_warning("Platform specific SsdUtil module not found. Falling down to the generic implementation")
80+
log.log_warning("Platform specific SsdUtil module not found. Falling down to the generic implementation") # noqa: E501
4181
try:
4282
from sonic_platform_base.sonic_storage.ssd import SsdUtil
4383
except ImportError as e:
44-
log.log_error("Failed to import default SsdUtil. Error: {}".format(str(e)), True)
84+
log.log_error("Failed to import default SsdUtil. Error: {}".format(str(e)), True) # noqa: E501
4585
raise e
4686

4787
return SsdUtil(diskdev)
4888

89+
4990
def is_number(s):
5091
try:
5192
float(s)
5293
return True
5394
except ValueError:
5495
return False
5596

97+
5698
# ==================== Entry point ====================
5799
def ssdutil():
58100
if os.geteuid() != 0:
59101
print("Root privileges are required for this operation")
60102
sys.exit(1)
61103

62104
parser = argparse.ArgumentParser()
63-
parser.add_argument("-d", "--device", help="Device name to show health info", default=DEFAULT_DEVICE)
64-
parser.add_argument("-v", "--verbose", action="store_true", default=False, help="Show verbose output (some additional parameters)")
65-
parser.add_argument("-e", "--vendor", action="store_true", default=False, help="Show vendor output (extended output if provided by platform vendor)")
105+
(default_device, disk_type) = get_default_disk()
106+
parser.add_argument("-d", "--device", help="Device name to show health info", default=default_device) # noqa: E501
107+
parser.add_argument("-v", "--verbose", action="store_true", default=False, help="Show verbose output (some additional parameters)") # noqa: E501
108+
parser.add_argument("-e", "--vendor", action="store_true", default=False, help="Show vendor output (extended output if provided by platform vendor)") # noqa: E501
66109
args = parser.parse_args()
67110

111+
print("Disk Type : {0}".format(disk_type.upper()))
68112
ssd = import_ssd_api(args.device)
69113

70114
print("Device Model : {}".format(ssd.get_model()))
71115
if args.verbose:
72116
print("Firmware : {}".format(ssd.get_firmware()))
73117
print("Serial : {}".format(ssd.get_serial()))
74-
print("Health : {}{}".format(ssd.get_health(), "%" if is_number(ssd.get_health()) else ""))
75-
print("Temperature : {}{}".format(ssd.get_temperature(), "C" if is_number(ssd.get_temperature()) else ""))
118+
print("Health : {}{}".format(ssd.get_health(), "%" if is_number(ssd.get_health()) else "")) # noqa: E501
119+
print("Temperature : {}{}".format(ssd.get_temperature(), "C" if is_number(ssd.get_temperature()) else "")) # noqa: E501
76120
if args.vendor:
77121
print(ssd.get_vendor_output())
78122

123+
79124
if __name__ == '__main__':
80125
ssdutil()

tests/mocked_libs/__init__.py

Whitespace-only changes.

tests/mocked_libs/blkinfo.py

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
mock_json_op = \
2+
[
3+
{
4+
"name": "sdx",
5+
"kname": "sdx",
6+
"fstype": "",
7+
"label": "",
8+
"mountpoint": "",
9+
"size": "3965714432",
10+
"maj:min": "8:0",
11+
"rm": "0",
12+
"model": "SMART EUSB",
13+
"vendor": "SMART EUSB",
14+
"serial": "SPG200807J1",
15+
"hctl": "2:0:0:0",
16+
"tran": "usb",
17+
"rota": "1",
18+
"type": "disk",
19+
"ro": "0",
20+
"owner": "",
21+
"group": "",
22+
"mode": "brw-rw----",
23+
"children": [
24+
{
25+
"name": "sdx1",
26+
"kname": "sdx1",
27+
"fstype": "ext4",
28+
"label": "",
29+
"mountpoint": "/host",
30+
"size": "3964665856",
31+
"maj:min": "8:1",
32+
"rm": "0",
33+
"model": " ",
34+
"vendor": " ",
35+
"serial": "",
36+
"hctl": "",
37+
"tran": "",
38+
"rota": "1",
39+
"type": "part",
40+
"ro": "0",
41+
"owner": "",
42+
"group": "",
43+
"mode": "brw-rw----",
44+
"children": [],
45+
"parents": ["sdx"],
46+
"statistics": {
47+
"major": "8",
48+
"minor": "1",
49+
"kname": "sdx1",
50+
"reads_completed": "22104",
51+
"reads_merged": "5299",
52+
"sectors_read": "1091502",
53+
"time_spent_reading_ms": "51711",
54+
"writes_completed": "11283",
55+
"writes_merged": "13401",
56+
"sectors_written": "443784",
57+
"time_spent_ writing": "133398",
58+
"ios_in_progress": "0",
59+
"time_spent_doing_ios_ms": "112040",
60+
"weighted_time_ios_ms": "112040",
61+
},
62+
}
63+
],
64+
"parents": [],
65+
"statistics": {
66+
"major": "8",
67+
"minor": "0",
68+
"kname": "sdx",
69+
"reads_completed": "22151",
70+
"reads_merged": "5299",
71+
"sectors_read": "1093606",
72+
"time_spent_reading_ms": "52005",
73+
"writes_completed": "11283",
74+
"writes_merged": "13401",
75+
"sectors_written": "443784",
76+
"time_spent_ writing": "133398",
77+
"ios_in_progress": "0",
78+
"time_spent_doing_ios_ms": "112220",
79+
"weighted_time_ios_ms": "112220",
80+
},
81+
}
82+
]
83+
84+
85+
class BlkDiskInfo:
86+
def __init__(self):
87+
return
88+
89+
def get_disks(self, filters):
90+
return mock_json_op

tests/mocked_libs/psutil.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from collections import namedtuple
2+
3+
4+
def disk_partitions():
5+
sdiskpart = namedtuple('sdiskpart', ['mountpoint', 'device'])
6+
return [sdiskpart(mountpoint="/host", device="/dev/sdx1")]

tests/ssdutil_test.py

+38
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
1+
import os
12
import sys
23
import argparse
34
from unittest.mock import patch, MagicMock
45
import sonic_platform_base # noqa: F401
56

7+
tests_path = os.path.dirname(os.path.abspath(__file__))
8+
9+
# Add mocked_libs path so that the file under test
10+
# can load mocked modules from there
11+
mocked_libs_path = os.path.join(tests_path, "mocked_libs") # noqa: E402,F401
12+
sys.path.insert(0, mocked_libs_path)
13+
14+
from .mocked_libs import psutil # noqa: E402,F401
15+
from .mocked_libs.blkinfo import BlkDiskInfo # noqa: E402,F401
16+
17+
sys.modules['os.stat'] = MagicMock()
18+
sys.modules['os.major'] = MagicMock(return_value=8)
619
sys.modules['sonic_platform'] = MagicMock()
720
sys.modules['sonic_platform_base.sonic_ssd.ssd_generic'] = MagicMock()
821

@@ -32,8 +45,33 @@ def get_vendor_output(self):
3245

3346
class TestSsdutil:
3447

48+
@patch('os.geteuid', MagicMock(return_value=0))
49+
@patch('os.stat', MagicMock(st_rdev=2049))
50+
@patch('os.major', MagicMock(return_value=8))
51+
def test_get_default_disk(self):
52+
(default_device, disk_type) = ssdutil.get_default_disk()
53+
54+
assert default_device == "/dev/sdx"
55+
assert disk_type == 'usb'
56+
57+
@patch('os.geteuid', MagicMock(return_value=0))
58+
@patch('os.stat', MagicMock(st_rdev=2049))
59+
@patch('os.major', MagicMock(return_value=8))
60+
@patch('psutil.disk_partitions', MagicMock(return_value=None))
61+
def test_get_default_disk_none_partitions(self):
62+
(default_device, disk_type) = ssdutil.get_default_disk()
63+
64+
assert default_device == "/dev/sda"
65+
assert disk_type is None
66+
67+
def test_is_number_valueerror(self):
68+
outcome = ssdutil.is_number("nope")
69+
assert outcome is False
70+
3571
@patch('sonic_py_common.device_info.get_paths_to_platform_and_hwsku_dirs', MagicMock(return_value=("test_path", ""))) # noqa: E501
3672
@patch('os.geteuid', MagicMock(return_value=0))
73+
@patch('os.stat', MagicMock(st_rdev=2049))
74+
@patch('os.major', MagicMock(return_value=8))
3775
def test_sonic_storage_path(self):
3876

3977
with patch('argparse.ArgumentParser.parse_args', MagicMock()) as mock_args: # noqa: E501

0 commit comments

Comments
 (0)