| # 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 site_utils, 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 = site_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) |
| |