137 lines
4.9 KiB
Python
137 lines
4.9 KiB
Python
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import logging, re
|
|
from autotest_lib.client.bin import test
|
|
from autotest_lib.client.bin import utils
|
|
from autotest_lib.client.common_lib import error
|
|
|
|
|
|
class hardware_Smartctl(test.test):
|
|
"""
|
|
Run smartctl to retrieve S.M.A.R.T attribute and report in keyval format.
|
|
"""
|
|
|
|
version = 1
|
|
|
|
_SMARTCTL_DEVICE_MODEL_PATTERN = 'Device Model: *(?P<model>[^ ].*)$'
|
|
_SMARTCTL_RESULT_PATTERN = '.*[P-][O-][S-][R-][C-][K-].*'
|
|
|
|
# Temporary table: This value should be in smartctl in March 2014
|
|
# http://sourceforge.net/apps/trac/smartmontools/ticket/272
|
|
_SMARTCTL_LOOKUP_TABLE = {
|
|
'SanDisk SSD i100': {
|
|
171 : 'Program_Fail_Count',
|
|
172 : 'Erase_Fail_Count',
|
|
173 : 'Average_Write_Erase_Count',
|
|
174 : 'Unexpected_Power_Loss_Count',
|
|
230 : 'Percent_Write_Erase_Count',
|
|
234 : 'Percent_Write_Erase_Count_BC'
|
|
}
|
|
}
|
|
|
|
def run_once(self, iteration=1, dev=''):
|
|
"""
|
|
Read S.M.A.R.T attribute from target device
|
|
|
|
@param dev: target device
|
|
"""
|
|
if dev == '':
|
|
logging.info('Run rootdev to determine boot device')
|
|
dev = utils.get_root_device()
|
|
|
|
logging.info(str('dev: %s' % dev))
|
|
|
|
# Skip this test if dev is an eMMC device without raising an error
|
|
if re.match('.*mmc.*', dev):
|
|
logging.info('Target device is an eMMC device. Skip testing')
|
|
self.write_perf_keyval({'device_model' : 'eMMC'})
|
|
return
|
|
|
|
last_result = ''
|
|
|
|
|
|
# run multiple time to test the firmware part that retrieve SMART value
|
|
for loop in range(1, iteration + 1):
|
|
cmd = 'smartctl -a -f brief %s' % dev
|
|
result = utils.run(cmd, ignore_status=True)
|
|
exit_status = result.exit_status
|
|
result_text = result.stdout
|
|
result_lines = result_text.split('\n')
|
|
|
|
# log all line if line count is different
|
|
# otherwise log only changed line
|
|
if result_text != last_result:
|
|
logging.info(str('Iteration #%d' % loop))
|
|
last_result_lines = last_result.split('\n')
|
|
if len(last_result_lines) != len(result_lines):
|
|
for line in result_lines:
|
|
logging.info(line)
|
|
else:
|
|
for i, line in enumerate(result_lines):
|
|
if line != last_result_lines[i]:
|
|
logging.info(line)
|
|
last_result = result_text
|
|
|
|
# Ignore error other than first two bits
|
|
if exit_status & 0x3:
|
|
# Error message should be in 4th line of the output
|
|
msg = 'Test failed with error: %s' % result_lines[3]
|
|
raise error.TestFail(msg)
|
|
|
|
logging.info(str('smartctl exit status: 0x%x' % exit_status))
|
|
|
|
# find drive model
|
|
lookup_table = {}
|
|
pattern = re.compile(self._SMARTCTL_DEVICE_MODEL_PATTERN)
|
|
for line in result_lines:
|
|
if pattern.match(line):
|
|
model = pattern.match(line).group('model')
|
|
for known_model in self._SMARTCTL_LOOKUP_TABLE:
|
|
if model.startswith(known_model):
|
|
lookup_table = self._SMARTCTL_LOOKUP_TABLE[known_model]
|
|
break
|
|
break
|
|
else:
|
|
raise error.TestFail('Can not find drive model')
|
|
|
|
# Example of smart ctl result
|
|
# ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE
|
|
# 12 Power_Cycle_Count -O---- 100 100 000 - 204
|
|
# use flag field to find a valid line
|
|
pattern = re.compile(self._SMARTCTL_RESULT_PATTERN)
|
|
keyval = {}
|
|
fail = []
|
|
for line in result_lines:
|
|
if not pattern.match(line):
|
|
continue
|
|
field = line.split()
|
|
|
|
id = int(field[0])
|
|
if id in lookup_table:
|
|
# look up table overwrite smartctl name
|
|
key = lookup_table[id]
|
|
else:
|
|
key = field[1] # ATTRIBUTE_NAME
|
|
if key == 'Unknown_Attribute':
|
|
key = "Smart_Attribute_ID_%d" % id
|
|
|
|
keyval[key] = field[7] # RAW_VALUE
|
|
|
|
# check for failing attribute
|
|
if field[6] != '-':
|
|
fail += [key]
|
|
|
|
if len(keyval) == 0:
|
|
raise error.TestFail(
|
|
'Test failed with error: Can not parse smartctl keyval')
|
|
|
|
if len(fail) > 0:
|
|
keyval['fail'] = fail
|
|
|
|
keyval['exit_status'] = exit_status
|
|
keyval['device_model'] = model
|
|
self.write_perf_keyval(keyval)
|
|
|