diff options
Diffstat (limited to 'meta/lib/oeqa/utils/commands.py')
| -rw-r--r-- | meta/lib/oeqa/utils/commands.py | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/meta/lib/oeqa/utils/commands.py b/meta/lib/oeqa/utils/commands.py new file mode 100644 index 0000000000..9b42620610 --- /dev/null +++ b/meta/lib/oeqa/utils/commands.py | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | # Copyright (c) 2013 Intel Corporation | ||
| 2 | # | ||
| 3 | # Released under the MIT license (see COPYING.MIT) | ||
| 4 | |||
| 5 | # DESCRIPTION | ||
| 6 | # This module is mainly used by scripts/oe-selftest and modules under meta/oeqa/selftest | ||
| 7 | # It provides a class and methods for running commands on the host in a convienent way for tests. | ||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | import os | ||
| 12 | import sys | ||
| 13 | import signal | ||
| 14 | import subprocess | ||
| 15 | import threading | ||
| 16 | import logging | ||
| 17 | |||
| 18 | class Command(object): | ||
| 19 | def __init__(self, command, bg=False, timeout=None, data=None, **options): | ||
| 20 | |||
| 21 | self.defaultopts = { | ||
| 22 | "stdout": subprocess.PIPE, | ||
| 23 | "stderr": subprocess.STDOUT, | ||
| 24 | "stdin": None, | ||
| 25 | "shell": False, | ||
| 26 | "bufsize": -1, | ||
| 27 | } | ||
| 28 | |||
| 29 | self.cmd = command | ||
| 30 | self.bg = bg | ||
| 31 | self.timeout = timeout | ||
| 32 | self.data = data | ||
| 33 | |||
| 34 | self.options = dict(self.defaultopts) | ||
| 35 | if isinstance(self.cmd, basestring): | ||
| 36 | self.options["shell"] = True | ||
| 37 | if self.data: | ||
| 38 | self.options['stdin'] = subprocess.PIPE | ||
| 39 | self.options.update(options) | ||
| 40 | |||
| 41 | self.status = None | ||
| 42 | self.output = None | ||
| 43 | self.error = None | ||
| 44 | self.thread = None | ||
| 45 | |||
| 46 | self.log = logging.getLogger("utils.commands") | ||
| 47 | |||
| 48 | def run(self): | ||
| 49 | self.process = subprocess.Popen(self.cmd, **self.options) | ||
| 50 | |||
| 51 | def commThread(): | ||
| 52 | self.output, self.error = self.process.communicate(self.data) | ||
| 53 | |||
| 54 | self.thread = threading.Thread(target=commThread) | ||
| 55 | self.thread.start() | ||
| 56 | |||
| 57 | self.log.debug("Running command '%s'" % self.cmd) | ||
| 58 | |||
| 59 | if not self.bg: | ||
| 60 | self.thread.join(self.timeout) | ||
| 61 | self.stop() | ||
| 62 | |||
| 63 | def stop(self): | ||
| 64 | if self.thread.isAlive(): | ||
| 65 | self.process.terminate() | ||
| 66 | # let's give it more time to terminate gracefully before killing it | ||
| 67 | self.thread.join(5) | ||
| 68 | if self.thread.isAlive(): | ||
| 69 | self.process.kill() | ||
| 70 | self.thread.join() | ||
| 71 | |||
| 72 | self.output = self.output.rstrip() | ||
| 73 | self.status = self.process.poll() | ||
| 74 | |||
| 75 | self.log.debug("Command '%s' returned %d as exit code." % (self.cmd, self.status)) | ||
| 76 | # logging the complete output is insane | ||
| 77 | # bitbake -e output is really big | ||
| 78 | # and makes the log file useless | ||
| 79 | if self.status: | ||
| 80 | lout = "\n".join(self.output.splitlines()[-20:]) | ||
| 81 | self.log.debug("Last 20 lines:\n%s" % lout) | ||
| 82 | |||
| 83 | |||
| 84 | class Result(object): | ||
| 85 | pass | ||
| 86 | |||
| 87 | def runCmd(command, ignore_status=False, timeout=None, **options): | ||
| 88 | |||
| 89 | result = Result() | ||
| 90 | |||
| 91 | cmd = Command(command, timeout=timeout, **options) | ||
| 92 | cmd.run() | ||
| 93 | |||
| 94 | result.command = command | ||
| 95 | result.status = cmd.status | ||
| 96 | result.output = cmd.output | ||
| 97 | result.pid = cmd.process.pid | ||
| 98 | |||
| 99 | if result.status and not ignore_status: | ||
| 100 | raise AssertionError("Command '%s' returned non-zero exit status %d:\n%s" % (command, result.status, result.output)) | ||
| 101 | |||
| 102 | return result | ||
| 103 | |||
| 104 | |||
| 105 | def bitbake(command, ignore_status=False, timeout=None, **options): | ||
| 106 | if isinstance(command, basestring): | ||
| 107 | cmd = "bitbake " + command | ||
| 108 | else: | ||
| 109 | cmd = [ "bitbake" ] + command | ||
| 110 | |||
| 111 | return runCmd(cmd, ignore_status, timeout, **options) | ||
| 112 | |||
| 113 | |||
| 114 | def get_bb_env(target=None): | ||
| 115 | if target: | ||
| 116 | return runCmd("bitbake -e %s" % target).output | ||
| 117 | else: | ||
| 118 | return runCmd("bitbake -e").output | ||
| 119 | |||
| 120 | def get_bb_var(var, target=None): | ||
| 121 | val = None | ||
| 122 | bbenv = get_bb_env(target) | ||
| 123 | for line in bbenv.splitlines(): | ||
| 124 | if line.startswith(var + "="): | ||
| 125 | val = line.split('=')[1] | ||
| 126 | val = val.replace('\"','') | ||
| 127 | break | ||
| 128 | return val | ||
| 129 | |||
| 130 | def get_test_layer(): | ||
| 131 | layers = get_bb_var("BBLAYERS").split() | ||
| 132 | testlayer = None | ||
| 133 | for l in layers: | ||
| 134 | if "/meta-selftest" in l and os.path.isdir(l): | ||
| 135 | testlayer = l | ||
| 136 | break | ||
| 137 | return testlayer | ||
