blob: 16ac7667d2948c3bd93960e1cf58926f609070a2 [file] [log] [blame]
# Copyright 2010 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.
"""Wrapper for Google Factory Tools (gooftool).
This module provides fast access to "gooftool".
"""
import os
import subprocess
import tempfile
import factory_common # pylint: disable=unused-import
from cros.factory.test.env import paths
from cros.factory.test import session
from cros.factory.utils import file_utils
from cros.factory.utils import type_utils
GOOFTOOL_HOME = '/usr/local/factory'
def run(command, ignore_status=False):
"""Runs a gooftool command.
Args:
command: Shell command to execute.
ignore_status: False to raise exception when execution result is not 0.
Returns:
(stdout, stderr, return_code) of the execution results.
Raises:
error.TestError: The error message in "ERROR:.*" form by command.
"""
session.console.info('Running gooftool: %s', command)
# We want the stderr goes to CONSOLE_LOG_PATH immediately, but tee only
# works with stdout; so here's a tiny trick to swap the handles.
swap_stdout_stderr = '3>&1 1>&2 2>&3'
# When using pipes, return code is from the last command; so we need to use
# a temporary file for the return code of first command.
console_log_path = paths.CONSOLE_LOG_PATH
file_utils.TryMakeDirs(os.path.dirname(console_log_path))
return_code_file = tempfile.NamedTemporaryFile()
system_cmd = ('(PATH=%s:$PATH %s %s || echo $? >"%s") | tee -a "%s"' %
(GOOFTOOL_HOME, command, swap_stdout_stderr,
return_code_file.name, console_log_path))
proc = subprocess.Popen(system_cmd,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
shell=True)
# The order of output is reversed because we swapped stdout and stderr.
(err, out) = proc.communicate()
# normalize output data
out = out or ''
err = err or ''
if out.endswith('\n'):
out = out[:-1]
if err.endswith('\n'):
err = err[:-1]
# build return code and log results
return_code_file.seek(0)
return_code = int(return_code_file.read() or '0')
return_code_file.close()
return_code = proc.wait() or return_code
message = ('gooftool result: %s (%s), message: %s' %
(('FAILED' if return_code else 'SUCCESS'),
return_code, '\n'.join([out, err]) or '(None)'))
session.console.info(message)
if return_code and (not ignore_status):
# try to parse "ERROR.*" from err & out.
exception_message = '\n'.join(
[error_message for error_message in err.splitlines()
if error_message.startswith('ERROR')]) or message
raise type_utils.TestFailure(exception_message)
return (out, err, return_code)