1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging, re 6from autotest_lib.client.bin import test 7from autotest_lib.client.bin import utils 8from autotest_lib.client.common_lib import error 9 10 11class hardware_Smartctl(test.test): 12 """ 13 Run smartctl to retrieve S.M.A.R.T attribute and report in keyval format. 14 """ 15 16 version = 1 17 18 _SMARTCTL_DEVICE_MODEL_PATTERN = 'Device Model: *(?P<model>[^ ].*)$' 19 _SMARTCTL_RESULT_PATTERN = '.*[P-][O-][S-][R-][C-][K-].*' 20 21 # Temporary table: This value should be in smartctl in March 2014 22 # http://sourceforge.net/apps/trac/smartmontools/ticket/272 23 _SMARTCTL_LOOKUP_TABLE = { 24 'SanDisk SSD i100': { 25 171 : 'Program_Fail_Count', 26 172 : 'Erase_Fail_Count', 27 173 : 'Average_Write_Erase_Count', 28 174 : 'Unexpected_Power_Loss_Count', 29 230 : 'Percent_Write_Erase_Count', 30 234 : 'Percent_Write_Erase_Count_BC' 31 } 32 } 33 34 def run_once(self, iteration=1, dev=''): 35 """ 36 Read S.M.A.R.T attribute from target device 37 38 @param dev: target device 39 """ 40 if dev == '': 41 logging.info('Run rootdev to determine boot device') 42 dev = utils.get_root_device() 43 44 logging.info(str('dev: %s' % dev)) 45 46 # Skip this test if dev is an eMMC device without raising an error 47 if re.match('.*mmc.*', dev): 48 logging.info('Target device is an eMMC device. Skip testing') 49 self.write_perf_keyval({'device_model' : 'eMMC'}) 50 return 51 52 last_result = '' 53 54 55 # run multiple time to test the firmware part that retrieve SMART value 56 for loop in range(1, iteration + 1): 57 cmd = 'smartctl -a -f brief %s' % dev 58 result = utils.run(cmd, ignore_status=True) 59 exit_status = result.exit_status 60 result_text = result.stdout 61 result_lines = result_text.split('\n') 62 63 # log all line if line count is different 64 # otherwise log only changed line 65 if result_text != last_result: 66 logging.info(str('Iteration #%d' % loop)) 67 last_result_lines = last_result.split('\n') 68 if len(last_result_lines) != len(result_lines): 69 for line in result_lines: 70 logging.info(line) 71 else: 72 for i, line in enumerate(result_lines): 73 if line != last_result_lines[i]: 74 logging.info(line) 75 last_result = result_text 76 77 # Ignore error other than first two bits 78 if exit_status & 0x3: 79 # Error message should be in 4th line of the output 80 msg = 'Test failed with error: %s' % result_lines[3] 81 raise error.TestFail(msg) 82 83 logging.info(str('smartctl exit status: 0x%x' % exit_status)) 84 85 # find drive model 86 lookup_table = {} 87 pattern = re.compile(self._SMARTCTL_DEVICE_MODEL_PATTERN) 88 for line in result_lines: 89 if pattern.match(line): 90 model = pattern.match(line).group('model') 91 for known_model in self._SMARTCTL_LOOKUP_TABLE: 92 if model.startswith(known_model): 93 lookup_table = self._SMARTCTL_LOOKUP_TABLE[known_model] 94 break 95 break 96 else: 97 raise error.TestFail('Can not find drive model') 98 99 # Example of smart ctl result 100 # ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE 101 # 12 Power_Cycle_Count -O---- 100 100 000 - 204 102 # use flag field to find a valid line 103 pattern = re.compile(self._SMARTCTL_RESULT_PATTERN) 104 keyval = {} 105 fail = [] 106 for line in result_lines: 107 if not pattern.match(line): 108 continue 109 field = line.split() 110 111 id = int(field[0]) 112 if id in lookup_table: 113 # look up table overwrite smartctl name 114 key = lookup_table[id] 115 else: 116 key = field[1] # ATTRIBUTE_NAME 117 if key == 'Unknown_Attribute': 118 key = "Smart_Attribute_ID_%d" % id 119 120 keyval[key] = field[7] # RAW_VALUE 121 122 # check for failing attribute 123 if field[6] != '-': 124 fail += [key] 125 126 if len(keyval) == 0: 127 raise error.TestFail( 128 'Test failed with error: Can not parse smartctl keyval') 129 130 if len(fail) > 0: 131 keyval['fail'] = fail 132 133 keyval['exit_status'] = exit_status 134 keyval['device_model'] = model 135 self.write_perf_keyval(keyval) 136 137