# 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[^ ].*)$' _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)