forked from sonic-net/sonic-buildimage
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add SSD Health API and generic implementation (sonic-net#47)
* Add SSD Health API and generic implementation Signed-off-by: Andriy Moroz <c_andriym@mellanox.com>
- Loading branch information
1 parent
33b037d
commit cc2dac5
Showing
4 changed files
with
233 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# | ||
# ssd_base.py | ||
# | ||
# Base class for implementing common SSD health features | ||
# | ||
|
||
|
||
class SsdBase(object): | ||
""" | ||
Base class for interfacing with a SSD | ||
""" | ||
def __init__(self, diskdev): | ||
""" | ||
Constructor | ||
Args: | ||
diskdev: Linux device name to get parameters for | ||
""" | ||
pass | ||
|
||
def get_health(self): | ||
""" | ||
Retrieves current disk health in percentages | ||
Returns: | ||
A float number of current ssd health | ||
e.g. 83.5 | ||
""" | ||
raise NotImplementedError | ||
|
||
def get_temperature(self): | ||
""" | ||
Retrieves current disk temperature in Celsius | ||
Returns: | ||
A float number of current temperature in Celsius | ||
e.g. 40.1 | ||
""" | ||
raise NotImplementedError | ||
|
||
def get_model(self): | ||
""" | ||
Retrieves model for the given disk device | ||
Returns: | ||
A string holding disk model as provided by the manufacturer | ||
""" | ||
raise NotImplementedError | ||
|
||
def get_firmware(self): | ||
""" | ||
Retrieves firmware version for the given disk device | ||
Returns: | ||
A string holding disk firmware version as provided by the manufacturer | ||
""" | ||
raise NotImplementedError | ||
|
||
def get_serial(self): | ||
""" | ||
Retrieves serial number for the given disk device | ||
Returns: | ||
A string holding disk serial number as provided by the manufacturer | ||
""" | ||
raise NotImplementedError | ||
|
||
def get_vendor_output(self): | ||
""" | ||
Retrieves vendor specific data for the given disk device | ||
Returns: | ||
A string holding some vendor specific disk information | ||
""" | ||
raise NotImplementedError |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
# | ||
# ssd_generic.py | ||
# | ||
# Generic implementation of the SSD health API | ||
# SSD models supported: | ||
# - InnoDisk | ||
# - StorFly | ||
# - Virtium | ||
|
||
try: | ||
import exceptions # Python 2 | ||
except ImportError: | ||
import builtins as exceptions # Python 3 | ||
try: | ||
import re | ||
import subprocess | ||
from .ssd_base import SsdBase | ||
except ImportError as e: | ||
raise ImportError (str(e) + "- required module not found") | ||
|
||
SMARTCTL = "smartctl {} -a" | ||
INNODISK = "iSmart -d {}" | ||
VIRTIUM = "SmartCmd -m {}" | ||
|
||
NOT_AVAILABLE = "N/A" | ||
|
||
|
||
class SsdUtil(SsdBase): | ||
""" | ||
Generic implementation of the SSD health API | ||
""" | ||
model = NOT_AVAILABLE | ||
serial = NOT_AVAILABLE | ||
firmware = NOT_AVAILABLE | ||
temperature = NOT_AVAILABLE | ||
health = NOT_AVAILABLE | ||
ssd_info = NOT_AVAILABLE | ||
vendor_ssd_info = NOT_AVAILABLE | ||
|
||
def __init__(self, diskdev): | ||
self.vendor_ssd_utility = { | ||
"Generic" : { "utility" : SMARTCTL, "parser" : self.parse_generic_ssd_info }, | ||
"InnoDisk" : { "utility" : INNODISK, "parser" : self.parse_innodisk_info }, | ||
"M.2" : { "utility" : INNODISK, "parser" : self.parse_innodisk_info }, | ||
"StorFly" : { "utility" : VIRTIUM, "parser" : self.parse_virtium_info }, | ||
"Virtium" : { "utility" : VIRTIUM, "parser" : self.parse_virtium_info } | ||
} | ||
|
||
self.dev = diskdev | ||
# Generic part | ||
self.fetch_generic_ssd_info(diskdev) | ||
self.parse_generic_ssd_info() | ||
|
||
# Known vendor part | ||
if self.model: | ||
model_short = self.model.split()[0] | ||
if self.vendor_ssd_utility.has_key(model_short): | ||
self.fetch_vendor_ssd_info(diskdev, model_short) | ||
self.parse_vendor_ssd_info(model_short) | ||
else: | ||
# No handler registered for this disk model | ||
pass | ||
else: | ||
# Failed to get disk model | ||
self.model = "Unknown" | ||
|
||
def _execute_shell(self, cmd): | ||
process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) | ||
output, error = process.communicate() | ||
return output | ||
|
||
def _parse_re(self, pattern, buffer): | ||
res_list = re.findall(pattern, buffer) | ||
return res_list[0] if res_list else NOT_AVAILABLE | ||
|
||
def fetch_generic_ssd_info(self, diskdev): | ||
self.ssd_info = self._execute_shell(self.vendor_ssd_utility["Generic"]["utility"].format(diskdev)) | ||
|
||
def parse_generic_ssd_info(self): | ||
self.model = self._parse_re('Device Model:\s*(.+?)\n', self.ssd_info) | ||
self.serial = self._parse_re('Serial Number:\s*(.+?)\n', self.ssd_info) | ||
self.firmware = self._parse_re('Firmware Version:\s*(.+?)\n', self.ssd_info) | ||
|
||
def parse_innodisk_info(self): | ||
self.health = self._parse_re('Health:\s*(.+?)%', self.vendor_ssd_info) | ||
self.temperature = self._parse_re('Temperature\s*\[\s*(.+?)\]', self.vendor_ssd_info) | ||
|
||
def parse_virtium_info(self): | ||
self.temperature = self._parse_re('Temperature_Celsius\s*\d*\s*(\d+?)\s+', self.vendor_ssd_info) | ||
nand_endurance = self._parse_re('NAND_Endurance\s*\d*\s*(\d+?)\s+', self.vendor_ssd_info) | ||
avg_erase_count = self._parse_re('Average_Erase_Count\s*\d*\s*(\d+?)\s+', self.vendor_ssd_info) | ||
try: | ||
self.health = 100 - (float(avg_erase_count) * 100 / float(nand_endurance)) | ||
except ValueError: | ||
pass | ||
|
||
def fetch_vendor_ssd_info(self, diskdev, model): | ||
self.vendor_ssd_info = self._execute_shell(self.vendor_ssd_utility[model]["utility"].format(diskdev)) | ||
|
||
def parse_vendor_ssd_info(self, model): | ||
self.vendor_ssd_utility[model]["parser"]() | ||
|
||
def get_health(self): | ||
""" | ||
Retrieves current disk health in percentages | ||
Returns: | ||
A float number of current ssd health | ||
e.g. 83.5 | ||
""" | ||
return self.health | ||
|
||
def get_temperature(self): | ||
""" | ||
Retrieves current disk temperature in Celsius | ||
Returns: | ||
A float number of current temperature in Celsius | ||
e.g. 40.1 | ||
""" | ||
return self.temperature | ||
|
||
def get_model(self): | ||
""" | ||
Retrieves model for the given disk device | ||
Returns: | ||
A string holding disk model as provided by the manufacturer | ||
""" | ||
return self.model | ||
|
||
def get_firmware(self): | ||
""" | ||
Retrieves firmware version for the given disk device | ||
Returns: | ||
A string holding disk firmware version as provided by the manufacturer | ||
""" | ||
return self.firmware | ||
|
||
def get_serial(self): | ||
""" | ||
Retrieves serial number for the given disk device | ||
Returns: | ||
A string holding disk serial number as provided by the manufacturer | ||
""" | ||
return self.serial | ||
|
||
def get_vendor_output(self): | ||
""" | ||
Retrieves vendor specific data for the given disk device | ||
Returns: | ||
A string holding some vendor specific disk information | ||
""" | ||
return self.vendor_ssd_info |