普通文本  |  137行  |  4.88 KB

# 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)