Skip to content

Commit cc2dac5

Browse files
andriymoroz-mlnxjleveque
authored andcommitted
Add SSD Health API and generic implementation (#47)
* Add SSD Health API and generic implementation Signed-off-by: Andriy Moroz <[email protected]>
1 parent 33b037d commit cc2dac5

File tree

4 files changed

+233
-0
lines changed

4 files changed

+233
-0
lines changed

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
'sonic_platform_base',
1717
'sonic_platform_base.sonic_eeprom',
1818
'sonic_platform_base.sonic_sfp',
19+
'sonic_platform_base.sonic_ssd',
1920
'sonic_psu',
2021
'sonic_sfp',
2122
],

sonic_platform_base/sonic_ssd/__init__.py

Whitespace-only changes.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#
2+
# ssd_base.py
3+
#
4+
# Base class for implementing common SSD health features
5+
#
6+
7+
8+
class SsdBase(object):
9+
"""
10+
Base class for interfacing with a SSD
11+
"""
12+
def __init__(self, diskdev):
13+
"""
14+
Constructor
15+
16+
Args:
17+
diskdev: Linux device name to get parameters for
18+
"""
19+
pass
20+
21+
def get_health(self):
22+
"""
23+
Retrieves current disk health in percentages
24+
25+
Returns:
26+
A float number of current ssd health
27+
e.g. 83.5
28+
"""
29+
raise NotImplementedError
30+
31+
def get_temperature(self):
32+
"""
33+
Retrieves current disk temperature in Celsius
34+
35+
Returns:
36+
A float number of current temperature in Celsius
37+
e.g. 40.1
38+
"""
39+
raise NotImplementedError
40+
41+
def get_model(self):
42+
"""
43+
Retrieves model for the given disk device
44+
45+
Returns:
46+
A string holding disk model as provided by the manufacturer
47+
"""
48+
raise NotImplementedError
49+
50+
def get_firmware(self):
51+
"""
52+
Retrieves firmware version for the given disk device
53+
54+
Returns:
55+
A string holding disk firmware version as provided by the manufacturer
56+
"""
57+
raise NotImplementedError
58+
59+
def get_serial(self):
60+
"""
61+
Retrieves serial number for the given disk device
62+
63+
Returns:
64+
A string holding disk serial number as provided by the manufacturer
65+
"""
66+
raise NotImplementedError
67+
68+
def get_vendor_output(self):
69+
"""
70+
Retrieves vendor specific data for the given disk device
71+
72+
Returns:
73+
A string holding some vendor specific disk information
74+
"""
75+
raise NotImplementedError
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#
2+
# ssd_generic.py
3+
#
4+
# Generic implementation of the SSD health API
5+
# SSD models supported:
6+
# - InnoDisk
7+
# - StorFly
8+
# - Virtium
9+
10+
try:
11+
import exceptions # Python 2
12+
except ImportError:
13+
import builtins as exceptions # Python 3
14+
try:
15+
import re
16+
import subprocess
17+
from .ssd_base import SsdBase
18+
except ImportError as e:
19+
raise ImportError (str(e) + "- required module not found")
20+
21+
SMARTCTL = "smartctl {} -a"
22+
INNODISK = "iSmart -d {}"
23+
VIRTIUM = "SmartCmd -m {}"
24+
25+
NOT_AVAILABLE = "N/A"
26+
27+
28+
class SsdUtil(SsdBase):
29+
"""
30+
Generic implementation of the SSD health API
31+
"""
32+
model = NOT_AVAILABLE
33+
serial = NOT_AVAILABLE
34+
firmware = NOT_AVAILABLE
35+
temperature = NOT_AVAILABLE
36+
health = NOT_AVAILABLE
37+
ssd_info = NOT_AVAILABLE
38+
vendor_ssd_info = NOT_AVAILABLE
39+
40+
def __init__(self, diskdev):
41+
self.vendor_ssd_utility = {
42+
"Generic" : { "utility" : SMARTCTL, "parser" : self.parse_generic_ssd_info },
43+
"InnoDisk" : { "utility" : INNODISK, "parser" : self.parse_innodisk_info },
44+
"M.2" : { "utility" : INNODISK, "parser" : self.parse_innodisk_info },
45+
"StorFly" : { "utility" : VIRTIUM, "parser" : self.parse_virtium_info },
46+
"Virtium" : { "utility" : VIRTIUM, "parser" : self.parse_virtium_info }
47+
}
48+
49+
self.dev = diskdev
50+
# Generic part
51+
self.fetch_generic_ssd_info(diskdev)
52+
self.parse_generic_ssd_info()
53+
54+
# Known vendor part
55+
if self.model:
56+
model_short = self.model.split()[0]
57+
if self.vendor_ssd_utility.has_key(model_short):
58+
self.fetch_vendor_ssd_info(diskdev, model_short)
59+
self.parse_vendor_ssd_info(model_short)
60+
else:
61+
# No handler registered for this disk model
62+
pass
63+
else:
64+
# Failed to get disk model
65+
self.model = "Unknown"
66+
67+
def _execute_shell(self, cmd):
68+
process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
69+
output, error = process.communicate()
70+
return output
71+
72+
def _parse_re(self, pattern, buffer):
73+
res_list = re.findall(pattern, buffer)
74+
return res_list[0] if res_list else NOT_AVAILABLE
75+
76+
def fetch_generic_ssd_info(self, diskdev):
77+
self.ssd_info = self._execute_shell(self.vendor_ssd_utility["Generic"]["utility"].format(diskdev))
78+
79+
def parse_generic_ssd_info(self):
80+
self.model = self._parse_re('Device Model:\s*(.+?)\n', self.ssd_info)
81+
self.serial = self._parse_re('Serial Number:\s*(.+?)\n', self.ssd_info)
82+
self.firmware = self._parse_re('Firmware Version:\s*(.+?)\n', self.ssd_info)
83+
84+
def parse_innodisk_info(self):
85+
self.health = self._parse_re('Health:\s*(.+?)%', self.vendor_ssd_info)
86+
self.temperature = self._parse_re('Temperature\s*\[\s*(.+?)\]', self.vendor_ssd_info)
87+
88+
def parse_virtium_info(self):
89+
self.temperature = self._parse_re('Temperature_Celsius\s*\d*\s*(\d+?)\s+', self.vendor_ssd_info)
90+
nand_endurance = self._parse_re('NAND_Endurance\s*\d*\s*(\d+?)\s+', self.vendor_ssd_info)
91+
avg_erase_count = self._parse_re('Average_Erase_Count\s*\d*\s*(\d+?)\s+', self.vendor_ssd_info)
92+
try:
93+
self.health = 100 - (float(avg_erase_count) * 100 / float(nand_endurance))
94+
except ValueError:
95+
pass
96+
97+
def fetch_vendor_ssd_info(self, diskdev, model):
98+
self.vendor_ssd_info = self._execute_shell(self.vendor_ssd_utility[model]["utility"].format(diskdev))
99+
100+
def parse_vendor_ssd_info(self, model):
101+
self.vendor_ssd_utility[model]["parser"]()
102+
103+
def get_health(self):
104+
"""
105+
Retrieves current disk health in percentages
106+
107+
Returns:
108+
A float number of current ssd health
109+
e.g. 83.5
110+
"""
111+
return self.health
112+
113+
def get_temperature(self):
114+
"""
115+
Retrieves current disk temperature in Celsius
116+
117+
Returns:
118+
A float number of current temperature in Celsius
119+
e.g. 40.1
120+
"""
121+
return self.temperature
122+
123+
def get_model(self):
124+
"""
125+
Retrieves model for the given disk device
126+
127+
Returns:
128+
A string holding disk model as provided by the manufacturer
129+
"""
130+
return self.model
131+
132+
def get_firmware(self):
133+
"""
134+
Retrieves firmware version for the given disk device
135+
136+
Returns:
137+
A string holding disk firmware version as provided by the manufacturer
138+
"""
139+
return self.firmware
140+
141+
def get_serial(self):
142+
"""
143+
Retrieves serial number for the given disk device
144+
145+
Returns:
146+
A string holding disk serial number as provided by the manufacturer
147+
"""
148+
return self.serial
149+
150+
def get_vendor_output(self):
151+
"""
152+
Retrieves vendor specific data for the given disk device
153+
154+
Returns:
155+
A string holding some vendor specific disk information
156+
"""
157+
return self.vendor_ssd_info

0 commit comments

Comments
 (0)