Skip to content

Commit 01512ec

Browse files
authored
[SSD]Enhance ssd_generic with more error handling to avoid python crash #271
Signed-off-by: Kebo Liu [email protected] Description Judge the self._parse_re return value, if it's N/A then stop further string slice handling to avoid a crash. Update the regular expression pattern for Innodisk SSD health, to handle the case that the output of the health section is different when its lifetime reaches the end. Motivation and Context Original code doesn't handle the case that self._parse_re returns N/A, it's assuming that self._parse_re will always return a none N/A value thus further handling the result w/o judge, which could in a crash. On Innodisk SSD, when the SSD remaining lifetime reaches the end, the output of the Health section will be a number w/o %, e.g. Health: 0.00 instead of Health: 95.0% in the normal case, need to update the regular expression to handle this case. How Has This Been Tested? UT test has been added. Tested the change on platforms with different types of SSD.
1 parent ac3e7f1 commit 01512ec

File tree

2 files changed

+271
-6
lines changed

2 files changed

+271
-6
lines changed

sonic_platform_base/sonic_ssd/ssd_generic.py

+28-6
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,40 @@ def fetch_generic_ssd_info(self, diskdev):
7676
def parse_generic_ssd_info(self):
7777
if "nvme" in self.dev:
7878
self.model = self._parse_re('Model Number:\s*(.+?)\n', self.ssd_info)
79-
health_raw = self._parse_re('Percentage Used\s*(.+?)\n', self.ssd_info).split()[-1]
80-
self.health = 100 - float(health_raw.strip('%'))
81-
self.temperature = float(self._parse_re('Temperature\s*(.+?)\n', self.ssd_info).split()[-2])
79+
80+
health_raw = self._parse_re('Percentage Used\s*(.+?)\n', self.ssd_info)
81+
if health_raw == NOT_AVAILABLE:
82+
self.health = NOT_AVAILABLE
83+
else:
84+
health_raw = health_raw.split()[-1]
85+
self.health = 100 - float(health_raw.strip('%'))
86+
87+
temp_raw = self._parse_re('Temperature\s*(.+?)\n', self.ssd_info)
88+
if temp_raw == NOT_AVAILABLE:
89+
self.temperature = NOT_AVAILABLE
90+
else:
91+
temp_raw = temp_raw.split()[-2]
92+
self.temperature = float(temp_raw)
8293
else:
8394
self.model = self._parse_re('Device Model:\s*(.+?)\n', self.ssd_info)
84-
self.health = self._parse_re('Remaining_Lifetime_Perc\s*(.+?)\n', self.ssd_info).split()[-1]
85-
self.temperature = self._parse_re('Temperature_Celsius\s*(.+?)\n', self.ssd_info).split()[-6]
95+
96+
health_raw = self._parse_re('Remaining_Lifetime_Perc\s*(.+?)\n', self.ssd_info)
97+
if health_raw == NOT_AVAILABLE:
98+
self.health = NOT_AVAILABLE
99+
else:
100+
self.health = health_raw.split()[-1]
101+
102+
temp_raw = self._parse_re('Temperature_Celsius\s*(.+?)\n', self.ssd_info)
103+
if temp_raw == NOT_AVAILABLE:
104+
self.temperature = NOT_AVAILABLE
105+
else:
106+
self.temperature = temp_raw.split()[-6]
107+
86108
self.serial = self._parse_re('Serial Number:\s*(.+?)\n', self.ssd_info)
87109
self.firmware = self._parse_re('Firmware Version:\s*(.+?)\n', self.ssd_info)
88110

89111
def parse_innodisk_info(self):
90-
self.health = self._parse_re('Health:\s*(.+?)%', self.vendor_ssd_info)
112+
self.health = self._parse_re('Health:\s*(.+?)%?', self.vendor_ssd_info)
91113
self.temperature = self._parse_re('Temperature\s*\[\s*(.+?)\]', self.vendor_ssd_info)
92114

93115
def parse_virtium_info(self):

tests/ssd_generic_test.py

+243
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,257 @@
7171
Num ErrCount SQId CmdId Status PELoc LBA NSID VS
7272
0 5275 0 0x0001 0x0004 - 0 1 -"""
7373

74+
output_ssd = """smartctl 6.6 2017-11-05 r4594 [x86_64-linux-5.10.0-8-2-amd64] (local build)
75+
Copyright (C) 2002-17, Bruce Allen, Christian Franke, www.smartmontools.org
76+
77+
=== START OF INFORMATION SECTION ===
78+
Model Family: 3IE3/3ME3/3ME4 SSDs
79+
Device Model: (S42) 3IE3
80+
Serial Number: BCA11712280210689
81+
LU WWN Device Id: 5 24693f 2ca215959
82+
Firmware Version: S16425i
83+
User Capacity: 16,013,942,784 bytes [16.0 GB]
84+
Sector Size: 512 bytes logical/physical
85+
Rotation Rate: Solid State Device
86+
Form Factor: 2.5 inches
87+
Device is: In smartctl database [for details use: -P show]
88+
ATA Version is: ATA8-ACS (minor revision not indicated)
89+
SATA Version is: SATA 3.0, 6.0 Gb/s (current: 6.0 Gb/s)
90+
Local Time is: Thu Mar 31 03:00:15 2022 UTC
91+
SMART support is: Available - device has SMART capability.
92+
SMART support is: Enabled
93+
94+
=== START OF READ SMART DATA SECTION ===
95+
SMART overall-health self-assessment test result: PASSED
96+
97+
General SMART Values:
98+
Offline data collection status: (0x00) Offline data collection activity
99+
was never started.
100+
Auto Offline Data Collection: Disabled.
101+
Total time to complete Offline
102+
data collection: ( 32) seconds.
103+
Offline data collection
104+
capabilities: (0x00) Offline data collection not supported.
105+
SMART capabilities: (0x0003) Saves SMART data before entering
106+
power-saving mode.
107+
Supports SMART auto save timer.
108+
Error logging capability: (0x00) Error logging NOT supported.
109+
No General Purpose Logging support.
110+
SCT capabilities: (0x0039) SCT Status supported.
111+
SCT Error Recovery Control supported.
112+
SCT Feature Control supported.
113+
SCT Data Table supported.
114+
115+
SMART Attributes Data Structure revision number: 16
116+
Vendor Specific SMART Attributes with Thresholds:
117+
ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
118+
1 Raw_Read_Error_Rate 0x0000 000 000 000 Old_age Offline - 0
119+
2 Throughput_Performance 0x0000 000 000 000 Old_age Offline - 0
120+
3 Spin_Up_Time 0x0000 000 000 000 Old_age Offline - 0
121+
5 Later_Bad_Block 0x0013 100 100 001 Pre-fail Always - 0
122+
7 Seek_Error_Rate 0x0000 000 000 000 Old_age Offline - 0
123+
8 Seek_Time_Performance 0x0000 000 000 000 Old_age Offline - 0
124+
9 Power_On_Hours 0x0002 030 000 000 Old_age Always - 26142
125+
10 Spin_Retry_Count 0x0000 000 000 000 Old_age Offline - 0
126+
12 Power_Cycle_Count 0x0002 148 000 000 Old_age Always - 7828
127+
163 Total_Bad_Block_Count 0x0000 000 000 000 Old_age Offline - 8
128+
168 SATA_PHY_Error_Count 0x0000 000 000 000 Old_age Offline - 0
129+
169 Remaining_Lifetime_Perc 0x0000 095 000 000 Old_age Offline - 95
130+
175 Bad_Cluster_Table_Count 0x0000 000 000 000 Old_age Offline - 0
131+
192 Power-Off_Retract_Count 0x0000 000 000 000 Old_age Offline - 0
132+
194 Temperature_Celsius 0x0000 030 100 000 Old_age Offline - 30 (2 100 0 0 0)
133+
197 Current_Pending_Sector 0x0012 000 100 000 Old_age Always - 0
134+
225 Data_Log_Write_Count 0x0000 000 029 000 Old_age Offline - 45712577
135+
240 Write_Head 0x0000 000 000 000 Old_age Offline - 0
136+
165 Max_Erase_Count 0x0002 220 001 000 Old_age Always - 988
137+
167 Average_Erase_Count 0x0002 213 001 000 Old_age Always - 981
138+
170 Spare_Block_Count 0x0003 100 001 000 Pre-fail Always - 146
139+
171 Program_Fail_Count 0x0002 000 001 000 Old_age Always - 0
140+
172 Erase_Fail_Count 0x0002 000 001 000 Old_age Always - 0
141+
176 RANGE_RECORD_Count 0x0000 100 001 000 Old_age Offline - 0
142+
187 Reported_Uncorrect 0x0002 000 001 000 Old_age Always - 0
143+
229 Flash_ID 0x0002 100 001 000 Old_age Always - 0x517693943a98
144+
232 Spares_Remaining_Perc 0x0003 100 001 000 Pre-fail Always - 0
145+
235 Later_Bad_Blk_Inf_R/W/E 0x0002 000 000 000 Old_age Always - 0 0 0
146+
241 Host_Writes_32MiB 0x0002 100 001 000 Old_age Always - 178564
147+
242 Host_Reads_32MiB 0x0002 100 001 000 Old_age Always - 760991
148+
149+
SMART Error Log not supported
150+
151+
SMART Self-test Log not supported
152+
153+
Selective Self-tests/Logging not supported"""
154+
155+
output_Innodisk_ssd = """smartctl 6.6 2017-11-05 r4594 [x86_64-linux-4.19.0-12-2-amd64] (local build)
156+
Copyright (C) 2002-17, Bruce Allen, Christian Franke, www.smartmontools.org
157+
158+
=== START OF INFORMATION SECTION ===
159+
Model Family: Innodisk 1ME3/3ME/3SE SSDs
160+
Device Model: InnoDisk Corp. - mSATA 3ME
161+
Serial Number: 20171126AAAA11730156
162+
Firmware Version: S140714
163+
User Capacity: 32,017,047,552 bytes [32.0 GB]
164+
Sector Size: 512 bytes logical/physical
165+
Rotation Rate: Solid State Device
166+
Form Factor: 2.5 inches
167+
Device is: In smartctl database [for details use: -P show]
168+
ATA Version is: ACS-2 (minor revision not indicated)
169+
SATA Version is: SATA 3.0, 6.0 Gb/s (current: 6.0 Gb/s)
170+
Local Time is: Thu Mar 31 08:24:17 2022 UTC
171+
SMART support is: Available - device has SMART capability.
172+
SMART support is: Enabled
173+
174+
=== START OF READ SMART DATA SECTION ===
175+
SMART overall-health self-assessment test result: PASSED
176+
177+
General SMART Values:
178+
Offline data collection status: (0x00) Offline data collection activity
179+
was never started.
180+
Auto Offline Data Collection: Disabled.
181+
Total time to complete Offline
182+
data collection: ( 32) seconds.
183+
Offline data collection
184+
capabilities: (0x00) Offline data collection not supported.
185+
SMART capabilities: (0x0003) Saves SMART data before entering
186+
power-saving mode.
187+
Supports SMART auto save timer.
188+
Error logging capability: (0x00) Error logging NOT supported.
189+
General Purpose Logging supported.
190+
SCT capabilities: (0x0039) SCT Status supported.
191+
SCT Error Recovery Control supported.
192+
SCT Feature Control supported.
193+
SCT Data Table supported.
194+
195+
SMART Attributes Data Structure revision number: 16
196+
Vendor Specific SMART Attributes with Thresholds:
197+
ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE
198+
1 Raw_Read_Error_Rate 0x0000 000 000 000 Old_age Offline - 0
199+
2 Throughput_Performance 0x0000 000 000 000 Old_age Offline - 0
200+
3 Spin_Up_Time 0x0000 000 000 000 Old_age Offline - 0
201+
5 Reallocated_Sector_Ct 0x0002 100 100 000 Old_age Always - 0
202+
7 Seek_Error_Rate 0x0000 000 000 000 Old_age Offline - 0
203+
8 Seek_Time_Performance 0x0000 000 000 000 Old_age Offline - 0
204+
9 Power_On_Hours 0x0002 100 100 000 Old_age Always - 32474
205+
10 Spin_Retry_Count 0x0000 000 000 000 Old_age Offline - 0
206+
12 Power_Cycle_Count 0x0002 100 100 000 Old_age Always - 297
207+
168 SATA_PHY_Error_Count 0x0000 000 000 000 Old_age Offline - 0
208+
169 Unknown_Innodisk_Attr 0x0000 000 000 000 Old_age Offline - 0x000000000000
209+
175 Bad_Cluster_Table_Count 0x0000 000 000 000 Old_age Offline - 0
210+
192 Power-Off_Retract_Count 0x0000 000 000 000 Old_age Offline - 0
211+
1 Raw_Read_Error_Rate 0x0000 000 000 000 Old_age Offline - 2199023255552
212+
197 Current_Pending_Sector 0x0000 000 000 000 Old_age Offline - 0
213+
240 Write_Head 0x0000 000 000 000 Old_age Offline - 0
214+
225 Unknown_Innodisk_Attr 0x0000 000 000 000 Old_age Offline - 0
215+
170 Bad_Block_Count 0x0003 100 100 --- Pre-fail Always - 0 47 0
216+
173 Erase_Count 0x0002 100 100 --- Old_age Always - 0 7280 7192
217+
229 Flash_ID 0x0002 100 100 --- Old_age Always - 0x50769394de98
218+
236 Unstable_Power_Count 0x0002 100 100 --- Old_age Always - 0
219+
235 Later_Bad_Block 0x0002 100 000 --- Old_age Always - 0
220+
176 Uncorr_RECORD_Count 0x0000 100 000 --- Old_age Offline - 0
221+
222+
Read SMART Log Directory failed: scsi error badly formed scsi parameters
223+
224+
SMART Error Log not supported
225+
226+
SMART Self-test Log not supported
227+
228+
Selective Self-tests/Logging not supported
229+
230+
"""
231+
232+
output_Innodisk_vendor_info = """********************************************************************************************
233+
* Innodisk iSMART V3.9.41 2018/05/25 *
234+
********************************************************************************************
235+
Model Name: InnoDisk Corp. - mSATA 3ME
236+
FW Version: S140714
237+
Serial Number: 20171126AAAA11730156
238+
Health: 0.00
239+
Capacity: 29.818199 GB
240+
P/E Cycle: 3000
241+
Lifespan : 0 (Years : 0 Months : 0 Days : 0)
242+
Write Protect: Disable
243+
InnoRobust: Enable
244+
--------------------------------------------------------------------------------------------
245+
ID SMART Attributes Value Raw Value
246+
--------------------------------------------------------------------------------------------
247+
[09] Power On Hours [32474] [0902006464DA7E0000000000]
248+
[0C] Power Cycle Count [ 297] [0C0200646429010000000000]
249+
[AA] Total Bad Block Count [ 47] [AA0300646400002F00000000]
250+
[AD] Erase Count Max. [ 7280] [AD02006464181C701C000000]
251+
[AD] Erase Count Avg. [ 7192] [AD02006464181C701C000000]
252+
[C2] Temperature [ 0] [000000000000000000000000]
253+
[EB] Later Bad Block [ 0] [EB0200640000000000000000]
254+
[EB] Read Block [ 0] [EB0200640000000000000000]
255+
[EB] Write Block [ 0] [EB0200640000000000000000]
256+
[EB] Erase Block [ 0] [EB0200640000000000000000]
257+
[EC] Unstable Power Count [ 0] [EC0200646400000000000000]
258+
"""
259+
260+
output_lack_info_ssd = """smartctl 7.2 2020-12-30 r5155 [x86_64-linux-5.10.0-8-2-amd64] (local build)
261+
Copyright (C) 2002-20, Bruce Allen, Christian Franke, www.smartmontools.org
262+
263+
=== START OF INFORMATION SECTION ===
264+
265+
=== START OF SMART DATA SECTION ===
266+
267+
0 5275 0 0x0001 0x0004 - 0 1 -"""
268+
74269
class TestSsdGeneric:
75270
@mock.patch('sonic_platform_base.sonic_ssd.ssd_generic.SsdUtil._execute_shell', mock.MagicMock(return_value=output_nvme_ssd))
76271
def test_nvme_ssd(self):
272+
# Test parsing nvme ssd info
77273
nvme_ssd = SsdUtil('/dev/nvme0n1')
78274
assert(nvme_ssd.get_health() == 100.0)
79275
assert(nvme_ssd.get_model() == 'SFPC020GM1EC2TO-I-5E-11P-STD')
80276
assert(nvme_ssd.get_firmware() == "COT6OQ")
81277
assert(nvme_ssd.get_temperature() == 37)
82278
assert(nvme_ssd.get_serial() == "A0221030722410000027")
83279

280+
@mock.patch('sonic_platform_base.sonic_ssd.ssd_generic.SsdUtil._execute_shell', mock.MagicMock(return_value=output_lack_info_ssd))
281+
def test_nvme_ssd_with_na_path(self):
282+
# Test parsing nvme ssd info which lack of expected sections
283+
nvme_ssd = SsdUtil('/dev/nvme0n1')
284+
assert(nvme_ssd.get_health() == 'N/A')
285+
assert(nvme_ssd.get_model() == 'N/A')
286+
assert(nvme_ssd.get_firmware() == "N/A")
287+
assert(nvme_ssd.get_temperature() == "N/A")
288+
assert(nvme_ssd.get_serial() == "N/A")
289+
290+
@mock.patch('sonic_platform_base.sonic_ssd.ssd_generic.SsdUtil._execute_shell', mock.MagicMock(return_value=output_ssd))
291+
def test_ssd(self):
292+
# Test parsing a normal ssd info
293+
ssd = SsdUtil('/dev/sda')
294+
assert(ssd.get_health() == '95')
295+
assert(ssd.get_model() == '(S42) 3IE3')
296+
assert(ssd.get_firmware() == 'S16425i')
297+
assert(ssd.get_temperature() == '30')
298+
assert(ssd.get_serial() == 'BCA11712280210689')
299+
300+
@mock.patch('sonic_platform_base.sonic_ssd.ssd_generic.SsdUtil._execute_shell', mock.MagicMock(return_value=output_lack_info_ssd))
301+
def test_ssd_with_na_path(self):
302+
# Test parsing normal ssd info which lack of expected sections
303+
ssd = SsdUtil('/dev/sda')
304+
assert(ssd.get_health() == 'N/A')
305+
assert(ssd.get_model() == 'N/A')
306+
assert(ssd.get_firmware() == "N/A")
307+
assert(ssd.get_temperature() == "N/A")
308+
assert(ssd.get_serial() == "N/A")
309+
310+
@mock.patch('sonic_platform_base.sonic_ssd.ssd_generic.SsdUtil._execute_shell', mock.MagicMock(return_value=output_Innodisk_ssd))
311+
def test_Innodisk_ssd(self):
312+
# Test parsing Innodisk ssd info
313+
Innodisk_ssd = SsdUtil('/dev/sda')
314+
assert(Innodisk_ssd.get_health() == 'N/A')
315+
assert(Innodisk_ssd.get_model() == 'InnoDisk Corp. - mSATA 3ME')
316+
assert(Innodisk_ssd.get_firmware() == "S140714")
317+
assert(Innodisk_ssd.get_temperature() == 'N/A')
318+
assert(Innodisk_ssd.get_serial() == "20171126AAAA11730156")
319+
320+
Innodisk_ssd.vendor_ssd_info = output_Innodisk_vendor_info
321+
Innodisk_ssd.parse_vendor_ssd_info('InnoDisk')
322+
assert(Innodisk_ssd.get_health() == '0')
323+
assert(Innodisk_ssd.get_model() == 'InnoDisk Corp. - mSATA 3ME')
324+
assert(Innodisk_ssd.get_firmware() == "S140714")
325+
assert(Innodisk_ssd.get_temperature() == '0')
326+
assert(Innodisk_ssd.get_serial() == "20171126AAAA11730156")
84327

0 commit comments

Comments
 (0)