summaryrefslogtreecommitdiffstats
path: root/meta/lib/oeqa/utils
diff options
context:
space:
mode:
Diffstat (limited to 'meta/lib/oeqa/utils')
-rw-r--r--meta/lib/oeqa/utils/__init__.py0
-rw-r--r--meta/lib/oeqa/utils/decorators.py50
-rw-r--r--meta/lib/oeqa/utils/httpserver.py33
-rw-r--r--meta/lib/oeqa/utils/qemurunner.py228
-rw-r--r--meta/lib/oeqa/utils/sshcontrol.py109
-rw-r--r--meta/lib/oeqa/utils/targetbuild.py63
6 files changed, 483 insertions, 0 deletions
diff --git a/meta/lib/oeqa/utils/__init__.py b/meta/lib/oeqa/utils/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/meta/lib/oeqa/utils/__init__.py
diff --git a/meta/lib/oeqa/utils/decorators.py b/meta/lib/oeqa/utils/decorators.py
new file mode 100644
index 0000000000..33fed5a10b
--- /dev/null
+++ b/meta/lib/oeqa/utils/decorators.py
@@ -0,0 +1,50 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# Some custom decorators that can be used by unittests
6# Most useful is skipUnlessPassed which can be used for
7# creating dependecies between two test methods.
8
9from oeqa.oetest import *
10
11class skipIfFailure(object):
12
13 def __init__(self,testcase):
14 self.testcase = testcase
15
16 def __call__(self,f):
17 def wrapped_f(*args):
18 if self.testcase in (oeRuntimeTest.testFailures or oeRuntimeTest.testErrors):
19 raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
20 return f(*args)
21 wrapped_f.__name__ = f.__name__
22 return wrapped_f
23
24class skipIfSkipped(object):
25
26 def __init__(self,testcase):
27 self.testcase = testcase
28
29 def __call__(self,f):
30 def wrapped_f(*args):
31 if self.testcase in oeRuntimeTest.testSkipped:
32 raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
33 return f(*args)
34 wrapped_f.__name__ = f.__name__
35 return wrapped_f
36
37class skipUnlessPassed(object):
38
39 def __init__(self,testcase):
40 self.testcase = testcase
41
42 def __call__(self,f):
43 def wrapped_f(*args):
44 if self.testcase in oeRuntimeTest.testSkipped or \
45 self.testcase in oeRuntimeTest.testFailures or \
46 self.testcase in oeRuntimeTest.testErrors:
47 raise unittest.SkipTest("Testcase dependency not met: %s" % self.testcase)
48 return f(*args)
49 wrapped_f.__name__ = f.__name__
50 return wrapped_f
diff --git a/meta/lib/oeqa/utils/httpserver.py b/meta/lib/oeqa/utils/httpserver.py
new file mode 100644
index 0000000000..f161a1bddd
--- /dev/null
+++ b/meta/lib/oeqa/utils/httpserver.py
@@ -0,0 +1,33 @@
1import SimpleHTTPServer
2import multiprocessing
3import os
4
5class HTTPServer(SimpleHTTPServer.BaseHTTPServer.HTTPServer):
6
7 def server_start(self, root_dir):
8 os.chdir(root_dir)
9 self.serve_forever()
10
11class HTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
12
13 def log_message(self, format_str, *args):
14 pass
15
16class HTTPService(object):
17
18 def __init__(self, root_dir, host=''):
19 self.root_dir = root_dir
20 self.host = host
21 self.port = 0
22
23 def start(self):
24 self.server = HTTPServer((self.host, self.port), HTTPRequestHandler)
25 if self.port == 0:
26 self.port = self.server.server_port
27 self.process = multiprocessing.Process(target=self.server.server_start, args=[self.root_dir])
28 self.process.start()
29
30 def stop(self):
31 self.server.server_close()
32 self.process.terminate()
33 self.process.join()
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py
new file mode 100644
index 0000000000..256cf3c6a8
--- /dev/null
+++ b/meta/lib/oeqa/utils/qemurunner.py
@@ -0,0 +1,228 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# This module provides a class for starting qemu images using runqemu.
6# It's used by testimage.bbclass.
7
8import subprocess
9import os
10import time
11import signal
12import re
13import socket
14import select
15import bb
16
17class QemuRunner:
18
19 def __init__(self, machine, rootfs, display = None, tmpdir = None, deploy_dir_image = None, logfile = None, boottime = 400, runqemutime = 60):
20 # Popen object
21 self.runqemu = None
22
23 self.machine = machine
24 self.rootfs = rootfs
25
26 self.qemupid = None
27 self.ip = None
28
29 self.display = display
30 self.tmpdir = tmpdir
31 self.deploy_dir_image = deploy_dir_image
32 self.logfile = logfile
33 self.boottime = boottime
34 self.runqemutime = runqemutime
35
36 self.create_socket()
37
38 def create_socket(self):
39
40 self.bootlog = ''
41 self.qemusock = None
42
43 try:
44 self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
45 self.server_socket.setblocking(0)
46 self.server_socket.bind(("127.0.0.1",0))
47 self.server_socket.listen(2)
48 self.serverport = self.server_socket.getsockname()[1]
49 bb.note("Created listening socket for qemu serial console on: 127.0.0.1:%s" % self.serverport)
50 except socket.error, msg:
51 self.server_socket.close()
52 bb.fatal("Failed to create listening socket: %s" %msg[1])
53
54
55 def log(self, msg):
56 if self.logfile:
57 with open(self.logfile, "a") as f:
58 f.write("%s" % msg)
59
60 def launch(self, qemuparams = None):
61
62 if self.display:
63 os.environ["DISPLAY"] = self.display
64 else:
65 bb.error("To start qemu I need a X desktop, please set DISPLAY correctly (e.g. DISPLAY=:1)")
66 return False
67 if not os.path.exists(self.rootfs):
68 bb.error("Invalid rootfs %s" % self.rootfs)
69 return False
70 if not os.path.exists(self.tmpdir):
71 bb.error("Invalid TMPDIR path %s" % self.tmpdir)
72 return False
73 else:
74 os.environ["OE_TMPDIR"] = self.tmpdir
75 if not os.path.exists(self.deploy_dir_image):
76 bb.error("Invalid DEPLOY_DIR_IMAGE path %s" % self.deploy_dir_image)
77 return False
78 else:
79 os.environ["DEPLOY_DIR_IMAGE"] = self.deploy_dir_image
80
81 # Set this flag so that Qemu doesn't do any grabs as SDL grabs interact
82 # badly with screensavers.
83 os.environ["QEMU_DONT_GRAB"] = "1"
84 self.qemuparams = 'bootparams="console=tty1 console=ttyS0,115200n8" qemuparams="-serial tcp:127.0.0.1:%s"' % self.serverport
85 if qemuparams:
86 self.qemuparams = self.qemuparams[:-1] + " " + qemuparams + " " + '\"'
87
88 launch_cmd = 'runqemu %s %s %s' % (self.machine, self.rootfs, self.qemuparams)
89 self.runqemu = subprocess.Popen(launch_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT,preexec_fn=os.setpgrp)
90
91 bb.note("runqemu started, pid is %s" % self.runqemu.pid)
92 bb.note("waiting at most %s seconds for qemu pid" % self.runqemutime)
93 endtime = time.time() + self.runqemutime
94 while not self.is_alive() and time.time() < endtime:
95 time.sleep(1)
96
97 if self.is_alive():
98 bb.note("qemu started - qemu procces pid is %s" % self.qemupid)
99 cmdline = open('/proc/%s/cmdline' % self.qemupid).read()
100 self.ip, _, self.host_ip = cmdline.split('ip=')[1].split(' ')[0].split(':')[0:3]
101 if not re.search("^((?:[0-9]{1,3}\.){3}[0-9]{1,3})$", self.ip):
102 bb.note("Couldn't get ip from qemu process arguments, I got '%s'" % self.ip)
103 bb.note("Here is the ps output:\n%s" % cmdline)
104 self.kill()
105 return False
106 bb.note("IP found: %s" % self.ip)
107 bb.note("Waiting at most %d seconds for login banner" % self.boottime )
108 endtime = time.time() + self.boottime
109 socklist = [self.server_socket]
110 reachedlogin = False
111 stopread = False
112 while time.time() < endtime and not stopread:
113 sread, swrite, serror = select.select(socklist, [], [], 5)
114 for sock in sread:
115 if sock is self.server_socket:
116 self.qemusock, addr = self.server_socket.accept()
117 self.qemusock.setblocking(0)
118 socklist.append(self.qemusock)
119 socklist.remove(self.server_socket)
120 bb.note("Connection from %s:%s" % addr)
121 else:
122 data = sock.recv(1024)
123 if data:
124 self.log(data)
125 self.bootlog += data
126 lastlines = "\n".join(self.bootlog.splitlines()[-2:])
127 if re.search("login:", lastlines):
128 stopread = True
129 reachedlogin = True
130 bb.note("Reached login banner")
131 else:
132 socklist.remove(sock)
133 sock.close()
134 stopread = True
135
136 if not reachedlogin:
137 bb.note("Target didn't reached login boot in %d seconds" % self.boottime)
138 lines = "\n".join(self.bootlog.splitlines()[-5:])
139 bb.note("Last 5 lines of text:\n%s" % lines)
140 bb.note("Check full boot log: %s" % self.logfile)
141 self.kill()
142 return False
143 else:
144 bb.note("Qemu pid didn't appeared in %s seconds" % self.runqemutime)
145 output = self.runqemu.stdout
146 self.kill()
147 bb.note("Output from runqemu:\n%s" % output.read())
148 return False
149
150 return self.is_alive()
151
152 def kill(self):
153
154 if self.runqemu:
155 bb.note("Sending SIGTERM to runqemu")
156 os.kill(-self.runqemu.pid,signal.SIGTERM)
157 endtime = time.time() + self.runqemutime
158 while self.runqemu.poll() is None and time.time() < endtime:
159 time.sleep(1)
160 if self.runqemu.poll() is None:
161 bb.note("Sending SIGKILL to runqemu")
162 os.kill(-self.runqemu.pid,signal.SIGKILL)
163 self.runqemu = None
164 if self.server_socket:
165 self.server_socket.close()
166 self.server_socket = None
167 self.qemupid = None
168 self.ip = None
169
170 def restart(self, qemuparams = None):
171 bb.note("Restarting qemu process")
172 if self.runqemu.poll() is None:
173 self.kill()
174 self.create_socket()
175 if self.launch(qemuparams):
176 return True
177 return False
178
179 def is_alive(self):
180 qemu_child = self.find_child(str(self.runqemu.pid))
181 if qemu_child:
182 self.qemupid = qemu_child[0]
183 if os.path.exists("/proc/" + str(self.qemupid)):
184 return True
185 return False
186
187 def find_child(self,parent_pid):
188 #
189 # Walk the process tree from the process specified looking for a qemu-system. Return its [pid'cmd]
190 #
191 ps = subprocess.Popen(['ps', 'axww', '-o', 'pid,ppid,command'], stdout=subprocess.PIPE).communicate()[0]
192 processes = ps.split('\n')
193 nfields = len(processes[0].split()) - 1
194 pids = {}
195 commands = {}
196 for row in processes[1:]:
197 data = row.split(None, nfields)
198 if len(data) != 3:
199 continue
200 if data[1] not in pids:
201 pids[data[1]] = []
202
203 pids[data[1]].append(data[0])
204 commands[data[0]] = data[2]
205
206 if parent_pid not in pids:
207 return []
208
209 parents = []
210 newparents = pids[parent_pid]
211 while newparents:
212 next = []
213 for p in newparents:
214 if p in pids:
215 for n in pids[p]:
216 if n not in parents and n not in next:
217 next.append(n)
218 if p not in parents:
219 parents.append(p)
220 newparents = next
221 #print "Children matching %s:" % str(parents)
222 for p in parents:
223 # Need to be careful here since runqemu-internal runs "ldd qemu-system-xxxx"
224 # Also, old versions of ldd (2.11) run "LD_XXXX qemu-system-xxxx"
225 basecmd = commands[p].split()[0]
226 basecmd = os.path.basename(basecmd)
227 if "qemu-system" in basecmd and "-serial tcp" in commands[p]:
228 return [int(p),commands[p]]
diff --git a/meta/lib/oeqa/utils/sshcontrol.py b/meta/lib/oeqa/utils/sshcontrol.py
new file mode 100644
index 0000000000..1539ff2a37
--- /dev/null
+++ b/meta/lib/oeqa/utils/sshcontrol.py
@@ -0,0 +1,109 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# Provides a class for setting up ssh connections,
6# running commands and copying files to/from a target.
7# It's used by testimage.bbclass and tests in lib/oeqa/runtime.
8
9import subprocess
10import time
11import os
12
13class SSHControl(object):
14
15 def __init__(self, host=None, timeout=300, logfile=None):
16 self.host = host
17 self.timeout = timeout
18 self._starttime = None
19 self._out = ''
20 self._ret = 126
21 self.logfile = logfile
22 self.ssh_options = [
23 '-o', 'UserKnownHostsFile=/dev/null',
24 '-o', 'StrictHostKeyChecking=no',
25 '-o', 'LogLevel=ERROR'
26 ]
27 self.ssh = ['ssh', '-l', 'root'] + self.ssh_options
28
29 def log(self, msg):
30 if self.logfile:
31 with open(self.logfile, "a") as f:
32 f.write("%s\n" % msg)
33
34 def _internal_run(self, cmd):
35 # We need this for a proper PATH
36 cmd = ". /etc/profile; " + cmd
37 command = self.ssh + [self.host, cmd]
38 self.log("[Running]$ %s" % " ".join(command))
39 self._starttime = time.time()
40 # ssh hangs without os.setsid
41 proc = subprocess.Popen(command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, preexec_fn=os.setsid)
42 return proc
43
44 def run(self, cmd, timeout=None):
45 """Run cmd and get it's return code and output.
46 Let it run for timeout seconds and then terminate/kill it,
47 if time is 0 will let cmd run until it finishes.
48 Time can be passed to here or can be set per class instance."""
49
50 if self.host:
51 sshconn = self._internal_run(cmd)
52 else:
53 raise Exception("Remote IP hasn't been set: '%s'" % actualcmd)
54
55 if timeout == 0:
56 self._out = sshconn.communicate()[0]
57 self._ret = sshconn.poll()
58 else:
59 if timeout is None:
60 tdelta = self.timeout
61 else:
62 tdelta = timeout
63 endtime = self._starttime + tdelta
64 while sshconn.poll() is None and time.time() < endtime:
65 time.sleep(1)
66 # process hasn't returned yet
67 if sshconn.poll() is None:
68 self._ret = 255
69 sshconn.terminate()
70 sshconn.kill()
71 self._out = sshconn.stdout.read()
72 sshconn.stdout.close()
73 self._out += "\n[!!! SSH command timed out after %d seconds and it was killed]" % tdelta
74 else:
75 self._out = sshconn.stdout.read()
76 self._ret = sshconn.poll()
77 # strip the last LF so we can test the output
78 self._out = self._out.rstrip()
79 self.log("%s" % self._out)
80 self.log("[SSH command returned after %d seconds]: %s" % (time.time() - self._starttime, self._ret))
81 return (self._ret, self._out)
82
83 def _internal_scp(self, cmd):
84 cmd = ['scp'] + self.ssh_options + cmd
85 self.log("[Running SCP]$ %s" % " ".join(cmd))
86 self._starttime = time.time()
87 scpconn = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, preexec_fn=os.setsid)
88 out = scpconn.communicate()[0]
89 ret = scpconn.poll()
90 self.log("%s" % out)
91 self.log("[SCP command returned after %d seconds]: %s" % (time.time() - self._starttime, ret))
92 if ret != 0:
93 # we raise an exception so that tests fail in setUp and setUpClass without a need for an assert
94 raise Exception("Error running %s, output: %s" % ( " ".join(cmd), out))
95 return (ret, out)
96
97 def copy_to(self, localpath, remotepath):
98 actualcmd = [localpath, 'root@%s:%s' % (self.host, remotepath)]
99 return self._internal_scp(actualcmd)
100
101 def copy_from(self, remotepath, localpath):
102 actualcmd = ['root@%s:%s' % (self.host, remotepath), localpath]
103 return self._internal_scp(actualcmd)
104
105 def get_status(self):
106 return self._ret
107
108 def get_output(self):
109 return self._out
diff --git a/meta/lib/oeqa/utils/targetbuild.py b/meta/lib/oeqa/utils/targetbuild.py
new file mode 100644
index 0000000000..9b2cf53773
--- /dev/null
+++ b/meta/lib/oeqa/utils/targetbuild.py
@@ -0,0 +1,63 @@
1# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# Provides a class for automating build tests for projects
6
7from oeqa.oetest import oeRuntimeTest
8import bb.fetch2
9import bb.data
10import os
11import re
12
13
14class TargetBuildProject():
15
16 def __init__(self, target, uri, foldername=None):
17 self.target = target
18 self.uri = uri
19 self.targetdir = "/home/root/"
20
21 self.localdata = bb.data.createCopy(oeRuntimeTest.tc.d)
22 bb.data.update_data(self.localdata)
23
24 if not foldername:
25 self.archive = os.path.basename(uri)
26 self.fname = re.sub(r'.tar.bz2|tar.gz$', '', self.archive)
27 else:
28 self.fname = foldername
29
30 def download_archive(self):
31
32 try:
33 self.localdata.delVar("BB_STRICT_CHECKSUM")
34 fetcher = bb.fetch2.Fetch([self.uri], self.localdata)
35 fetcher.download()
36 self.localarchive = fetcher.localpath(self.uri)
37 except bb.fetch2.BBFetchException:
38 raise Exception("Failed to download archive: %s" % self.uri)
39
40 (status, output) = self.target.copy_to(self.localarchive, self.targetdir)
41 if status != 0:
42 raise Exception("Failed to copy archive to target, output: %s" % output)
43
44 (status, output) = self.target.run('tar xf %s%s -C %s' % (self.targetdir, self.archive, self.targetdir))
45 if status != 0:
46 raise Exception("Failed to extract archive, output: %s" % output)
47
48 #Change targetdir to project folder
49 self.targetdir = self.targetdir + self.fname
50
51 # The timeout parameter of target.run is set to 0 to make the ssh command
52 # run with no timeout.
53 def run_configure(self):
54 return self.target.run('cd %s; ./configure' % self.targetdir, 0)[0]
55
56 def run_make(self):
57 return self.target.run('cd %s; make' % self.targetdir, 0)[0]
58
59 def run_install(self):
60 return self.target.run('cd %s; make install' % self.targetdir, 0)[0]
61
62 def clean(self):
63 self.target.run('rm -rf %s' % self.targetdir)