blob: 2af7a4cdf786ac480f178517f412d4c2fdfc37d1 [file] [log] [blame]
# 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, time, traceback
from functools import partial
from autotest_lib.client.common_lib import error
from autotest_lib.server import autotest
from autotest_lib.server import hosts
from autotest_lib.server import test
class hardware_StorageStress(test.test):
"""
Integrity stress test for storage device
"""
version = 1
# Define default value for the test case
_TEST_GAP = 60 # 1 min
_TEST_DURATION = 12 * 60 * 60 # 12 hours
_FIO_REQUIREMENT_FILE = '8k_async_randwrite'
_FIO_WRITE_FLAGS = []
_FIO_VERIFY_FLAGS = ['--verifyonly']
def run_once(self, client_ip, gap=_TEST_GAP, duration=_TEST_DURATION,
power_command='reboot', storage_test_command='integrity',
storage_test_argument=''):
"""
Run the Storage stress test
Use hardwareStorageFio to run some test_command repeatedly for a long
time. Between each iteration of test command, run power command such as
reboot or suspend.
@param client_ip: string of client's ip address (required)
@param gap: gap between each test (second) default = 1 min
@param duration: duration to run test (second) default = 12 hours
@param power_command: command to do between each test Command
possible command: reboot / suspend / nothing
@param storage_test_command: FIO command to run
- integrity: Check data integrity
- full_write: Check performance consistency
for full disk write. Use argument
to determine which disk to write
"""
# init test
if not client_ip:
error.TestError("Must provide client's IP address to test")
self._client = hosts.create_host(client_ip)
self._client_at = autotest.Autotest(self._client)
self._results = {}
start_time = time.time()
# parse power command
if power_command == 'nothing':
power_func = self._do_nothing
elif power_command == 'reboot':
power_func = self._do_reboot
elif power_command == 'suspend':
power_func = self._do_suspend
else:
raise error.TestFail(
'Test failed with error: Invalid power command')
# parse test command
if storage_test_command == 'integrity':
setup_func = self._write_data
loop_func = self._verify_data
elif storage_test_command == 'full_write':
setup_func = self._do_nothing
loop_func = partial(self._full_disk_write,
dev=storage_test_argument)
else:
raise error.TestFail('Test failed with error: Invalid test command')
setup_func()
# init statistic variable
min_time_per_loop = self._TEST_DURATION
max_time_per_loop = 0
all_loop_time = 0
avr_time_per_loop = 0
self._loop_count = 0
while time.time() - start_time < duration:
# sleep
time.sleep(gap)
self._loop_count += 1
# do power command & verify data & calculate time
loop_start_time = time.time()
power_func()
loop_func()
loop_time = time.time() - loop_start_time
# update statistic
all_loop_time += loop_time
min_time_per_loop = min(loop_time, min_time_per_loop)
max_time_per_loop = max(loop_time, max_time_per_loop)
if self._loop_count > 0:
avr_time_per_loop = all_loop_time / self._loop_count
logging.info(str('check data count: %d' % self._loop_count))
# report result
self.write_perf_keyval({'loop_count':self._loop_count})
self.write_perf_keyval({'min_time_per_loop':min_time_per_loop})
self.write_perf_keyval({'max_time_per_loop':max_time_per_loop})
self.write_perf_keyval({'avr_time_per_loop':avr_time_per_loop})
def _do_nothing(self):
pass
def _do_reboot(self):
"""
Reboot host machine
"""
logging.info('Server: reboot client')
try:
self._client.reboot()
except error.AutoservRebootError as e:
raise error.TestFail('%s.\nTest failed with error %s' % (
traceback.format_exc(), str(e)))
def _do_suspend(self):
"""
Suspend host machine
"""
logging.info('Server: suspend client')
self._client_at.run_test('power_Resume')
passed = self._check_client_test_result(self._client)
if not passed:
raise error.TestFail('Test failed with error: Suspend Error')
def _check_client_test_result(self, client):
"""
Check result of the client test.
Auto test will store results in the file named status.
We check that the second to last line in that file begin with 'END GOOD'
@ return True if last test passed, False otherwise.
"""
client_result_dir = '%s/results/default' % client.autodir
command = 'tail -2 %s/status | head -1' % client_result_dir
status = client.run(command).stdout.strip()
logging.info(status)
return status[:8] == 'END GOOD'
def _write_data(self):
"""
Write test data to host using hardware_StorageFio
"""
logging.info('_write_data')
result_dir = 'hardware_StorageFio_write'
self._client_at.run_test('hardware_StorageFio', wait=0,
results_dir=result_dir,
requirements=[(self._FIO_REQUIREMENT_FILE,
self._FIO_WRITE_FLAGS)])
passed = self._check_client_test_result(self._client)
if not passed:
raise error.TestFail('Test failed with error: Data Write Error')
def _verify_data(self):
"""
Vertify test data using hardware_StorageFio
"""
logging.info(str('_verify_data #%d' % self._loop_count))
result_dir = str('hardware_StorageFio_verify_%d'
% self._loop_count)
self._client_at.run_test('hardware_StorageFio', wait=0,
results_dir=result_dir,
requirements=[(self._FIO_REQUIREMENT_FILE,
self._FIO_VERIFY_FLAGS)])
passed = self._check_client_test_result(self._client)
if not passed:
raise error.TestFail('Test failed with error: Data Verify #%d Error'
% self._loop_count)
def _full_disk_write(self, dev):
"""
Do the root device full area write and report performance
"""
logging.info(str('_full_disk_write #%d' % self._loop_count))
logging.info(str('target device "%s"' % dev))
# check sanity of target device: begin with /dev/ and can find with ls
if dev[0:5] != '/dev/':
raise error.TestFail(
'Test failed with error: device should begin with /dev/')
# This command return 0 when device exist, return 2 otherwise
cmd = 'ls %s >/dev/null 2>&1' % dev
if self._client.run(cmd, ignore_status=True).exit_status:
raise error.TestFail(
'Test failed with error: device does not exist')
# log some hardware status in the first run
if self._loop_count == 1:
cmd = 'cat /sys/class/block/%s/device/type' % dev[5:]
type = self._client.run(cmd).stdout.strip()
if type == '0': #scsi disk
cmd = 'smartctl -x %s' % dev
logging.info(self._client.run(cmd, ignore_status=True).stdout)
elif type == 'MMC':
for field in ['cid', 'csd', 'name', 'serial']:
cmd = 'cat /sys/block/%s/device/%s' % (dev[5:], field)
result = self._client.run(cmd).stdout.strip()
logging.info(str('%s: %s' % (field, result)))
else:
raise error.TestFail('Unknown device type')
# determine current boot device
cur_dev = self._client.run('rootdev -s -d').stdout.strip()
logging.info(str('current boot device "%s"' % cur_dev))
if dev == cur_dev:
raise error.TestFail(
'Test failed with error: can not test boot device')
result_dir = str('hardware_StorageFio_full_disk_write_%d'
% self._loop_count)
self._client_at.run_test('hardware_StorageFio', dev=dev, filesize=0,
results_dir=result_dir,
requirements=[('64k_stress', [])])
passed = self._check_client_test_result(self._client)
if not passed:
raise error.TestFail(
"Test failed with error: Full disk Write #%d Error"
% self._loop_count)