diff options
author | Richard Purdie <richard.purdie@linuxfoundation.org> | 2015-09-04 16:59:38 +0100 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2015-09-06 15:26:18 +0100 |
commit | ef0fe3193eb546757f7c98274b88c4f2eafd7ad0 (patch) | |
tree | 4e3222722ee7fa29409fde8e2f614b8d12683a0e /meta/lib/oeqa | |
parent | 3d81a56912fb2c6ca1c5aafb93cbc6e2cfea20b3 (diff) | |
download | poky-ef0fe3193eb546757f7c98274b88c4f2eafd7ad0.tar.gz |
oeqa: Test failure/cleanup improvements
Currently, if qemu segfaults, the tests merrily continue trying to execute
which takes time for them to timeout and is a bit silly. Worse, no logs about
the segfault are shown to the user, its silent!
This patch tries to unravel the tangled web of issues and ensures that we:
* install a SIGCHLD handler which tells the user qemu exited
* check if qemu is running, if it isn't fail the test outright
* don't leave processes behind in sshcontrol which would hold
bitbake.lock and block shutdown
(From OE-Core rev: 5c04b1ca1e989f569d5755a646734d01a0c56cae)
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/oeqa')
-rw-r--r-- | meta/lib/oeqa/oetest.py | 3 | ||||
-rw-r--r-- | meta/lib/oeqa/targetcontrol.py | 3 | ||||
-rw-r--r-- | meta/lib/oeqa/utils/qemurunner.py | 35 | ||||
-rw-r--r-- | meta/lib/oeqa/utils/sshcontrol.py | 14 |
4 files changed, 43 insertions, 12 deletions
diff --git a/meta/lib/oeqa/oetest.py b/meta/lib/oeqa/oetest.py index 9724325083..ff62c30268 100644 --- a/meta/lib/oeqa/oetest.py +++ b/meta/lib/oeqa/oetest.py | |||
@@ -144,6 +144,9 @@ class oeRuntimeTest(oeTest): | |||
144 | self.target = oeRuntimeTest.tc.target | 144 | self.target = oeRuntimeTest.tc.target |
145 | super(oeRuntimeTest, self).__init__(methodName) | 145 | super(oeRuntimeTest, self).__init__(methodName) |
146 | 146 | ||
147 | def setUp(self): | ||
148 | self.assertTrue(self.target.check(), msg = "Qemu not running?") | ||
149 | |||
147 | def tearDown(self): | 150 | def tearDown(self): |
148 | # If a test fails or there is an exception | 151 | # If a test fails or there is an exception |
149 | if not exc_info() == (None, None, None): | 152 | if not exc_info() == (None, None, None): |
diff --git a/meta/lib/oeqa/targetcontrol.py b/meta/lib/oeqa/targetcontrol.py index 542e259112..edc0d01c1e 100644 --- a/meta/lib/oeqa/targetcontrol.py +++ b/meta/lib/oeqa/targetcontrol.py | |||
@@ -188,6 +188,9 @@ class QemuTarget(BaseTarget): | |||
188 | bb.error("Qemu log output from %s:\n%s" % (self.qemulog, f.read())) | 188 | bb.error("Qemu log output from %s:\n%s" % (self.qemulog, f.read())) |
189 | raise bb.build.FuncFailed("%s - FAILED to start qemu - check the task log and the boot log" % self.pn) | 189 | raise bb.build.FuncFailed("%s - FAILED to start qemu - check the task log and the boot log" % self.pn) |
190 | 190 | ||
191 | def check(self): | ||
192 | return self.runner.is_alive() | ||
193 | |||
191 | def stop(self): | 194 | def stop(self): |
192 | self.runner.stop() | 195 | self.runner.stop() |
193 | self.connection = None | 196 | self.connection = None |
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py index 3ad747a503..2d485379e8 100644 --- a/meta/lib/oeqa/utils/qemurunner.py +++ b/meta/lib/oeqa/utils/qemurunner.py | |||
@@ -64,6 +64,22 @@ class QemuRunner: | |||
64 | with open(self.logfile, "a") as f: | 64 | with open(self.logfile, "a") as f: |
65 | f.write("%s" % msg) | 65 | f.write("%s" % msg) |
66 | 66 | ||
67 | def getOutput(self, o): | ||
68 | import fcntl | ||
69 | fl = fcntl.fcntl(o, fcntl.F_GETFL) | ||
70 | fcntl.fcntl(o, fcntl.F_SETFL, fl | os.O_NONBLOCK) | ||
71 | return os.read(o.fileno(), 1000000) | ||
72 | |||
73 | |||
74 | def handleSIGCHLD(self, signum, frame): | ||
75 | if self.runqemu and self.runqemu.poll(): | ||
76 | if self.runqemu.returncode: | ||
77 | logger.info('runqemu exited with code %d' % self.runqemu.returncode) | ||
78 | logger.info("Output from runqemu:\n%s" % self.getOutput(self.runqemu.stdout)) | ||
79 | self.stop() | ||
80 | self._dump_host() | ||
81 | raise SystemExit | ||
82 | |||
67 | def start(self, qemuparams = None): | 83 | def start(self, qemuparams = None): |
68 | if self.display: | 84 | if self.display: |
69 | os.environ["DISPLAY"] = self.display | 85 | os.environ["DISPLAY"] = self.display |
@@ -98,11 +114,8 @@ class QemuRunner: | |||
98 | if qemuparams: | 114 | if qemuparams: |
99 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' | 115 | self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"' |
100 | 116 | ||
101 | def getOutput(o): | 117 | self.origchldhandler = signal.getsignal(signal.SIGCHLD) |
102 | import fcntl | 118 | signal.signal(signal.SIGCHLD, self.handleSIGCHLD) |
103 | fl = fcntl.fcntl(o, fcntl.F_GETFL) | ||
104 | fcntl.fcntl(o, fcntl.F_SETFL, fl | os.O_NONBLOCK) | ||
105 | return os.read(o.fileno(), 1000000) | ||
106 | 119 | ||
107 | launch_cmd = 'runqemu tcpserial=%s %s %s %s' % (self.serverport, self.machine, self.rootfs, self.qemuparams) | 120 | launch_cmd = 'runqemu tcpserial=%s %s %s %s' % (self.serverport, self.machine, self.rootfs, self.qemuparams) |
108 | # FIXME: We pass in stdin=subprocess.PIPE here to work around stty | 121 | # FIXME: We pass in stdin=subprocess.PIPE here to work around stty |
@@ -122,7 +135,7 @@ class QemuRunner: | |||
122 | logger.info('runqemu exited with code %d' % self.runqemu.returncode) | 135 | logger.info('runqemu exited with code %d' % self.runqemu.returncode) |
123 | self._dump_host() | 136 | self._dump_host() |
124 | self.stop() | 137 | self.stop() |
125 | logger.info("Output from runqemu:\n%s" % getOutput(output)) | 138 | logger.info("Output from runqemu:\n%s" % self.getOutput(output)) |
126 | return False | 139 | return False |
127 | time.sleep(1) | 140 | time.sleep(1) |
128 | 141 | ||
@@ -139,7 +152,7 @@ class QemuRunner: | |||
139 | self.ip = ips[0] | 152 | self.ip = ips[0] |
140 | self.server_ip = ips[1] | 153 | self.server_ip = ips[1] |
141 | except IndexError, ValueError: | 154 | except IndexError, ValueError: |
142 | logger.info("Couldn't get ip from qemu process arguments! Here is the qemu command line used:\n%s\nand output from runqemu:\n%s" % (cmdline, getOutput(output))) | 155 | logger.info("Couldn't get ip from qemu process arguments! Here is the qemu command line used:\n%s\nand output from runqemu:\n%s" % (cmdline, self.getOutput(output))) |
143 | self._dump_host() | 156 | self._dump_host() |
144 | self.stop() | 157 | self.stop() |
145 | return False | 158 | return False |
@@ -154,7 +167,7 @@ class QemuRunner: | |||
154 | logger.error("Didn't receive a console connection from qemu. " | 167 | logger.error("Didn't receive a console connection from qemu. " |
155 | "Here is the qemu command line used:\n%s\nand " | 168 | "Here is the qemu command line used:\n%s\nand " |
156 | "output from runqemu:\n%s" % (cmdline, | 169 | "output from runqemu:\n%s" % (cmdline, |
157 | getOutput(output))) | 170 | self.getOutput(output))) |
158 | self.stop_thread() | 171 | self.stop_thread() |
159 | return False | 172 | return False |
160 | 173 | ||
@@ -213,15 +226,15 @@ class QemuRunner: | |||
213 | logger.info("Qemu pid didn't appeared in %s seconds" % self.runqemutime) | 226 | logger.info("Qemu pid didn't appeared in %s seconds" % self.runqemutime) |
214 | self._dump_host() | 227 | self._dump_host() |
215 | self.stop() | 228 | self.stop() |
216 | logger.info("Output from runqemu:\n%s" % getOutput(output)) | 229 | logger.info("Output from runqemu:\n%s" % self.getOutput(output)) |
217 | return False | 230 | return False |
218 | 231 | ||
219 | return self.is_alive() | 232 | return self.is_alive() |
220 | 233 | ||
221 | def stop(self): | 234 | def stop(self): |
222 | |||
223 | self.stop_thread() | 235 | self.stop_thread() |
224 | if self.runqemu: | 236 | if self.runqemu: |
237 | signal.signal(signal.SIGCHLD, self.origchldhandler) | ||
225 | logger.info("Sending SIGTERM to runqemu") | 238 | logger.info("Sending SIGTERM to runqemu") |
226 | try: | 239 | try: |
227 | os.killpg(self.runqemu.pid, signal.SIGTERM) | 240 | os.killpg(self.runqemu.pid, signal.SIGTERM) |
@@ -255,6 +268,8 @@ class QemuRunner: | |||
255 | return False | 268 | return False |
256 | 269 | ||
257 | def is_alive(self): | 270 | def is_alive(self): |
271 | if not self.runqemu: | ||
272 | return False | ||
258 | qemu_child = self.find_child(str(self.runqemu.pid)) | 273 | qemu_child = self.find_child(str(self.runqemu.pid)) |
259 | if qemu_child: | 274 | if qemu_child: |
260 | self.qemupid = qemu_child[0] | 275 | self.qemupid = qemu_child[0] |
diff --git a/meta/lib/oeqa/utils/sshcontrol.py b/meta/lib/oeqa/utils/sshcontrol.py index 6ed48badc8..00f5051053 100644 --- a/meta/lib/oeqa/utils/sshcontrol.py +++ b/meta/lib/oeqa/utils/sshcontrol.py | |||
@@ -42,7 +42,7 @@ class SSHProcess(object): | |||
42 | with open(self.logfile, "a") as f: | 42 | with open(self.logfile, "a") as f: |
43 | f.write("%s" % msg) | 43 | f.write("%s" % msg) |
44 | 44 | ||
45 | def run(self, command, timeout=None, logfile=None): | 45 | def _run(self, command, timeout=None, logfile=None): |
46 | self.logfile = logfile | 46 | self.logfile = logfile |
47 | self.starttime = time.time() | 47 | self.starttime = time.time() |
48 | output = '' | 48 | output = '' |
@@ -79,8 +79,18 @@ class SSHProcess(object): | |||
79 | 79 | ||
80 | self.status = self.process.wait() | 80 | self.status = self.process.wait() |
81 | self.output = output.rstrip() | 81 | self.output = output.rstrip() |
82 | return (self.status, self.output) | ||
83 | 82 | ||
83 | def run(self, command, timeout=None, logfile=None): | ||
84 | try: | ||
85 | self._run(command, timeout, logfile) | ||
86 | except: | ||
87 | # Need to guard against a SystemExit or other exception occuring whilst running | ||
88 | # and ensure we don't leave a process behind. | ||
89 | if self.process.poll() is None: | ||
90 | self.process.kill() | ||
91 | self.status = self.process.wait() | ||
92 | raise | ||
93 | return (self.status, self.output) | ||
84 | 94 | ||
85 | class SSHControl(object): | 95 | class SSHControl(object): |
86 | def __init__(self, ip, logfile=None, timeout=300, user='root', port=None): | 96 | def __init__(self, ip, logfile=None, timeout=300, user='root', port=None): |