summaryrefslogtreecommitdiff
path: root/automated/utils
diff options
context:
space:
mode:
authorDan Rue <dan.rue@linaro.org>2017-06-07 16:29:09 -0500
committerDan Rue <dan.rue@linaro.org>2017-06-12 19:00:59 +0000
commita9eb01c021428bcea8c8a3906b72fc8e5c13cb5a (patch)
tree6bfdce4ef8865402d26fdfe37e8f28fd9d66ec0a /automated/utils
parentd0919863e503785cb85d5c5e7d57b29e874eb165 (diff)
test-runner: Report environmental information
Add a dictionary key named 'environment' that identifies the host running the tests, whether that host is local or remote. For example, on a HiSilicon D03 system, the following data structure will be added to each result in results.json: { "bios_version": "Hisilicon D03 UEFI 16.12 Release", "board_name": "D03", "board_vendor": "Huawei", "kernel": "4.9.0-20.gitedc2a1c.linaro.aarch64", "linux_distribution": "centos", "packages": [ "GeoIP-1.5.0-11.el7", "NetworkManager-1.4.0-20.el7_3", ... "yum-plugin-fastestmirror-1.1.31-40.el7", "zlib-1.2.7-17.el7" ], "uname": "Linux localhost.localdomain 4.9.0-20.gitedc2a1c.linaro.aarch64 #1 SMP Wed Dec 14 17:50:15 UTC 2016 aarch64 aarch64 aarch64 GNU/Linux" } Support exists for Ubuntu, Debian, CentOS, and Fedora. Other distributions will print a warning and report an empty package list. Environmental data is added automatically in each test and may be skipped by passing -e or --skip-environment. This change led to a slight refactor in the way remote commands were being handled. Instead of percolating remote vs local logic everywhere, run_command will run an arbitrary shell command either locally or on the target host, and return the output. This allows all shell commands, whether local or remote, to be handled consistently. Change-Id: Ia94695751a04a7fc862a299526d33784c57a9893 Signed-off-by: Dan Rue <dan.rue@linaro.org>
Diffstat (limited to 'automated/utils')
-rwxr-xr-xautomated/utils/test-runner.py151
1 files changed, 131 insertions, 20 deletions
diff --git a/automated/utils/test-runner.py b/automated/utils/test-runner.py
index c374c26..88d4177 100755
--- a/automated/utils/test-runner.py
+++ b/automated/utils/test-runner.py
@@ -28,10 +28,16 @@ except ImportError as e:
SSH_PARAMS = "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
-def call_ssh(args):
- ssh_cmd = "ssh %s %s" % (SSH_PARAMS, args)
- ssh_output = subprocess.check_output(shlex.split(ssh_cmd)).strip()
- return ssh_output
+def run_command(command, target=None):
+ """ Run a shell command. If target is specified, ssh to the given target first. """
+
+ run = command
+ if target:
+ run = 'ssh {} {} "{}"'.format(SSH_PARAMS, target, command)
+
+ logger = logging.getLogger('RUNNER.run_command')
+ logger.debug(run)
+ return subprocess.check_output(shlex.split(run)).strip().decode('utf-8')
class TestPlan(object):
@@ -293,19 +299,26 @@ class RemoteTestRun(AutomatedTestRun):
def copy_to_target(self):
os.chdir(self.test['test_path'])
tarball_name = "target-test-files.tar"
- tar_cmd = 'tar -caf %s run.sh uuid automated/lib automated/bin automated/utils %s' % (tarball_name, self.test['tc_relative_dir'])
- subprocess.call(shlex.split(tar_cmd))
- create_target_test_path_cmd = '%s "mkdir -p %s"' % (self.args.target, self.test['target_test_path'])
- call_ssh(create_target_test_path_cmd)
- scp_cmd = 'scp %s ./%s %s:%s' % (SSH_PARAMS, tarball_name, self.args.target, self.test['target_test_path'])
- self.logger.info('Pushing test files to target with command: %s' % scp_cmd)
- subprocess.call(shlex.split(scp_cmd))
- uncompress_cmd = '%s "cd %s && tar -xf %s"' % (self.args.target, self.test['target_test_path'], tarball_name)
- self.logger.info('Uncompressing test files on target with command: %s' % uncompress_cmd)
- call_ssh(uncompress_cmd)
- delete_tarball_cmd = "%s rm %s/%s" % (self.args.target, self.test['target_test_path'], tarball_name)
- self.logger.info("Deleting remote tarball: %s" % delete_tarball_cmd)
- call_ssh(delete_tarball_cmd)
+
+ self.logger.info("Archiving test files")
+ run_command(
+ 'tar -caf %s run.sh uuid automated/lib automated/bin automated/utils %s' %
+ (tarball_name, self.test['tc_relative_dir']))
+
+ self.logger.info("Creating test path")
+ run_command("mkdir -p %s" % (self.test['target_test_path']), self.args.target)
+
+ self.logger.info("Copying test archive to target host")
+ run_command('scp %s ./%s %s:%s' % (SSH_PARAMS, tarball_name, self.args.target,
+ self.test['target_test_path']))
+
+ self.logger.info("Unarchiving test files on target")
+ run_command("cd %s && tar -xf %s" % (self.test['target_test_path'],
+ tarball_name), self.args.target)
+
+ self.logger.info("Removing test file archive from target")
+ run_command("rm %s/%s" % (self.test['target_test_path'],
+ tarball_name), self.args.target)
def run(self):
self.copy_to_target()
@@ -432,6 +445,98 @@ class ManualTestRun(TestRun, cmd.Cmd):
pass
+def get_packages(linux_distribution, target=None):
+ """ Return a list of installed packages with versions
+
+ linux_distribution is a string that may be 'debian',
+ 'ubuntu', 'centos', or 'fedora'.
+
+ For example (ubuntu):
+ 'packages': ['acl-2.2.52-2',
+ 'adduser-3.113+nmu3',
+ ...
+ 'zlib1g:amd64-1:1.2.8.dfsg-2+b1',
+ 'zlib1g-dev:amd64-1:1.2.8.dfsg-2+b1']
+
+ (centos):
+ "packages": ["acl-2.2.51-12.el7",
+ "apr-1.4.8-3.el7",
+ ...
+ "zlib-1.2.7-17.el7",
+ "zlib-devel-1.2.7-17.el7"
+ ]
+ """
+
+ logger = logging.getLogger('RUNNER.get_packages')
+ packages = []
+ if linux_distribution in ['debian', 'ubuntu']:
+ # Debian (apt) based system
+ packages = run_command("dpkg-query -W -f '${package}-${version}\n'", target).splitlines()
+
+ elif linux_distribution in ['centos', 'fedora']:
+ # RedHat (rpm) based system
+ packages = run_command("rpm -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}\n'", target).splitlines()
+ else:
+ logger.warning("Unknown linux distribution '{}'; package list not populated.".format(linux_distribution))
+
+ packages.sort()
+ return packages
+
+
+def get_environment(target=None, skip_collection=False):
+ """ Return a dictionary with environmental information
+
+ target: optional ssh host string to gather environment remotely.
+ skip_collection: Skip data collection and return an empty dictionary.
+
+ For example (on a HiSilicon D03):
+ {
+ "bios_version": "Hisilicon D03 UEFI 16.12 Release",
+ "board_name": "D03",
+ "board_vendor": "Huawei",
+ "kernel": "4.9.0-20.gitedc2a1c.linaro.aarch64",
+ "linux_distribution": "centos",
+ "packages": [
+ "GeoIP-1.5.0-11.el7",
+ "NetworkManager-1.4.0-20.el7_3",
+ ...
+ "yum-plugin-fastestmirror-1.1.31-40.el7",
+ "zlib-1.2.7-17.el7"
+ ],
+ "uname": "Linux localhost.localdomain 4.9.0-20.gitedc2a1c.linaro.aarch64 #1 SMP Wed Dec 14 17:50:15 UTC 2016 aarch64 aarch64 aarch64 GNU/Linux"
+ }
+ """
+
+ environment = {}
+ if skip_collection:
+ return environment
+ environment['linux_distribution'] = run_command(
+ "grep ^ID= /etc/os-release", target).split('=')[-1].strip('"').lower()
+ environment['kernel'] = run_command("uname -r", target)
+ environment['uname'] = run_command("uname -a", target)
+
+ try:
+ environment['bios_version'] = run_command(
+ "cat /sys/devices/virtual/dmi/id/bios_version", target)
+ except subprocess.CalledProcessError:
+ environment['bios_version'] = ""
+
+ try:
+ environment['board_vendor'] = run_command(
+ "cat /sys/devices/virtual/dmi/id/board_vendor", target)
+ except subprocess.CalledProcessError:
+ environment['board_vendor'] = ""
+
+ try:
+ environment['board_name'] = run_command(
+ "cat /sys/devices/virtual/dmi/id/board_name", target)
+ except subprocess.CalledProcessError:
+ environment['board_name'] = ""
+
+ environment['packages'] = get_packages(environment['linux_distribution'], target)
+ return environment
+
+
class ResultParser(object):
def __init__(self, test, args):
self.test = test
@@ -440,6 +545,9 @@ class ResultParser(object):
self.results = {}
self.results['test'] = test['test_name']
self.results['id'] = test['test_uuid']
+ self.results['test_plan'] = args.test_plan
+ self.results['environment'] = get_environment(
+ target=self.args.target, skip_collection=self.args.skip_environment)
self.logger = logging.getLogger('RUNNER.ResultParser')
self.results['params'] = {}
self.pattern = None
@@ -615,6 +723,9 @@ def get_args():
parser.add_argument('-s', '--skip_install', dest='skip_install',
default=False, action='store_true',
help='skip install section defined in test definition.')
+ parser.add_argument('-e', '--skip_environment', dest='skip_environment',
+ default=False, action='store_true',
+ help='skip environmental data collection (board name, distro, etc)')
args = parser.parse_args()
return args
@@ -646,7 +757,7 @@ def main():
logger.error('openssh client must be installed on the host.')
sys.exit(1)
try:
- call_ssh("%s exit" % args.target)
+ run_command("exit", args.target)
except subprocess.CalledProcessError as e:
logger.error('ssh login failed.')
print(e)
@@ -674,8 +785,7 @@ def main():
tc_realpath = os.path.realpath(test['path'])
tc_dirname = os.path.dirname(tc_realpath)
test['tc_relative_dir'] = '%s%s' % (args.kind, tc_dirname.split(args.kind)[1])
- target_user_home_cmd = '%s "echo $HOME"' % args.target
- target_user_home = call_ssh(target_user_home_cmd)
+ target_user_home = run_command("echo $HOME", args.target)
test['target_test_path'] = '%s/output/%s' % (target_user_home, test['test_uuid'])
logger.debug('Test parameters: %s' % test)
@@ -702,5 +812,6 @@ def main():
else:
logger.warning("Requested test definition %s doesn't exist" % test['path'])
+
if __name__ == "__main__":
main()