summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta/classes/testimage.bbclass39
-rw-r--r--meta/lib/oeqa/core/target/qemu.py7
-rw-r--r--meta/lib/oeqa/utils/qemurunner.py31
3 files changed, 70 insertions, 7 deletions
diff --git a/meta/classes/testimage.bbclass b/meta/classes/testimage.bbclass
index 0d55c3247e..75f0f2c3e3 100644
--- a/meta/classes/testimage.bbclass
+++ b/meta/classes/testimage.bbclass
@@ -34,6 +34,17 @@ TESTIMAGE_AUTO ??= "0"
34# TEST_QEMUPARAMS can be used to pass extra parameters to qemu, e.g. "-m 1024" for setting the amount of ram to 1 GB. 34# TEST_QEMUPARAMS can be used to pass extra parameters to qemu, e.g. "-m 1024" for setting the amount of ram to 1 GB.
35# TEST_RUNQEMUPARAMS can be used to pass extra parameters to runqemu, e.g. "gl" to enable OpenGL acceleration. 35# TEST_RUNQEMUPARAMS can be used to pass extra parameters to runqemu, e.g. "gl" to enable OpenGL acceleration.
36 36
37# TESTIMAGE_BOOT_PATTERNS can be used to override certain patterns used to communicate with the target when booting,
38# if a pattern is not specifically present on this variable a default will be used when booting the target.
39# TESTIMAGE_BOOT_PATTERNS[<flag>] overrides the pattern used for that specific flag, where flag comes from a list of accepted flags
40# e.g. normally the system boots and waits for a login prompt (login:), after that it sends the command: "root\n" to log as the root user
41# if we wanted to log in as the hypothetical "webserver" user for example we could set the following:
42# TESTIMAGE_BOOT_PATTERNS = "send_login_user search_login_succeeded"
43# TESTIMAGE_BOOT_PATTERNS[send_login_user] = "webserver\n"
44# TESTIMAGE_BOOT_PATTERNS[search_login_succeeded] = "webserver@[a-zA-Z0-9\-]+:~#"
45# The accepted flags are the following: search_reached_prompt, send_login_user, search_login_succeeded, search_cmd_finished.
46# They are prefixed with either search/send, to differentiate if the pattern is meant to be sent or searched to/from the target terminal
47
37TEST_LOG_DIR ?= "${WORKDIR}/testimage" 48TEST_LOG_DIR ?= "${WORKDIR}/testimage"
38 49
39TEST_EXPORT_DIR ?= "${TMPDIR}/testimage/${PN}" 50TEST_EXPORT_DIR ?= "${TMPDIR}/testimage/${PN}"
@@ -68,6 +79,8 @@ TEST_TARGET ?= "qemu"
68TEST_QEMUPARAMS ?= "" 79TEST_QEMUPARAMS ?= ""
69TEST_RUNQEMUPARAMS ?= "" 80TEST_RUNQEMUPARAMS ?= ""
70 81
82TESTIMAGE_BOOT_PATTERNS ?= ""
83
71TESTIMAGEDEPENDS = "" 84TESTIMAGEDEPENDS = ""
72TESTIMAGEDEPENDS_append_qemuall = " qemu-native:do_populate_sysroot qemu-helper-native:do_populate_sysroot qemu-helper-native:do_addto_recipe_sysroot" 85TESTIMAGEDEPENDS_append_qemuall = " qemu-native:do_populate_sysroot qemu-helper-native:do_populate_sysroot qemu-helper-native:do_addto_recipe_sysroot"
73TESTIMAGEDEPENDS += "${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'cpio-native:do_populate_sysroot', '', d)}" 86TESTIMAGEDEPENDS += "${@bb.utils.contains('IMAGE_PKGTYPE', 'rpm', 'cpio-native:do_populate_sysroot', '', d)}"
@@ -150,6 +163,29 @@ def get_testimage_json_result_dir(d):
150def get_testimage_result_id(configuration): 163def get_testimage_result_id(configuration):
151 return '%s_%s_%s_%s' % (configuration['TEST_TYPE'], configuration['IMAGE_BASENAME'], configuration['MACHINE'], configuration['STARTTIME']) 164 return '%s_%s_%s_%s' % (configuration['TEST_TYPE'], configuration['IMAGE_BASENAME'], configuration['MACHINE'], configuration['STARTTIME'])
152 165
166def get_testimage_boot_patterns(d):
167 from collections import defaultdict
168 boot_patterns = defaultdict(str)
169 # Only accept certain values
170 accepted_patterns = ['search_reached_prompt', 'send_login_user', 'search_login_succeeded', 'search_cmd_finished']
171 # Not all patterns need to be overriden, e.g. perhaps we only want to change the user
172 boot_patterns_flags = d.getVarFlags('TESTIMAGE_BOOT_PATTERNS') or {}
173 if boot_patterns_flags:
174 patterns_set = [p for p in boot_patterns_flags.items() if p[0] in d.getVar('TESTIMAGE_BOOT_PATTERNS').split()]
175 for flag, flagval in patterns_set:
176 if flag not in accepted_patterns:
177 bb.fatal('Testimage: The only accepted boot patterns are: search_reached_prompt,send_login_user, \
178 search_login_succeeded,search_cmd_finished\n Make sure your TESTIMAGE_BOOT_PATTERNS=%s \
179 contains an accepted flag.' % d.getVar('TESTIMAGE_BOOT_PATTERNS'))
180 return
181 # We know boot prompt is searched through in binary format, others might be expressions
182 if flag == 'search_reached_prompt':
183 boot_patterns[flag] = flagval.encode()
184 else:
185 boot_patterns[flag] = flagval.encode().decode('unicode-escape')
186 return boot_patterns
187
188
153def testimage_main(d): 189def testimage_main(d):
154 import os 190 import os
155 import json 191 import json
@@ -259,6 +295,9 @@ def testimage_main(d):
259 'serial_ports': len(d.getVar("SERIAL_CONSOLES").split()), 295 'serial_ports': len(d.getVar("SERIAL_CONSOLES").split()),
260 } 296 }
261 297
298 if d.getVar("TESTIMAGE_BOOT_PATTERNS"):
299 target_kwargs['boot_patterns'] = get_testimage_boot_patterns(d)
300
262 # TODO: Currently BBPATH is needed for custom loading of targets. 301 # TODO: Currently BBPATH is needed for custom loading of targets.
263 # It would be better to find these modules using instrospection. 302 # It would be better to find these modules using instrospection.
264 target_kwargs['target_modules_path'] = d.getVar('BBPATH') 303 target_kwargs['target_modules_path'] = d.getVar('BBPATH')
diff --git a/meta/lib/oeqa/core/target/qemu.py b/meta/lib/oeqa/core/target/qemu.py
index 008a9f03ce..059106e915 100644
--- a/meta/lib/oeqa/core/target/qemu.py
+++ b/meta/lib/oeqa/core/target/qemu.py
@@ -8,6 +8,7 @@ import os
8import sys 8import sys
9import signal 9import signal
10import time 10import time
11from collections import defaultdict
11 12
12from .ssh import OESSHTarget 13from .ssh import OESSHTarget
13from oeqa.utils.qemurunner import QemuRunner 14from oeqa.utils.qemurunner import QemuRunner
@@ -18,7 +19,8 @@ class OEQemuTarget(OESSHTarget):
18 def __init__(self, logger, server_ip, timeout=300, user='root', 19 def __init__(self, logger, server_ip, timeout=300, user='root',
19 port=None, machine='', rootfs='', kernel='', kvm=False, slirp=False, 20 port=None, machine='', rootfs='', kernel='', kvm=False, slirp=False,
20 dump_dir='', dump_host_cmds='', display='', bootlog='', 21 dump_dir='', dump_host_cmds='', display='', bootlog='',
21 tmpdir='', dir_image='', boottime=60, serial_ports=2, **kwargs): 22 tmpdir='', dir_image='', boottime=60, serial_ports=2,
23 boot_patterns = defaultdict(str), **kwargs):
22 24
23 super(OEQemuTarget, self).__init__(logger, None, server_ip, timeout, 25 super(OEQemuTarget, self).__init__(logger, None, server_ip, timeout,
24 user, port) 26 user, port)
@@ -30,13 +32,14 @@ class OEQemuTarget(OESSHTarget):
30 self.kernel = kernel 32 self.kernel = kernel
31 self.kvm = kvm 33 self.kvm = kvm
32 self.use_slirp = slirp 34 self.use_slirp = slirp
35 self.boot_patterns = boot_patterns
33 36
34 self.runner = QemuRunner(machine=machine, rootfs=rootfs, tmpdir=tmpdir, 37 self.runner = QemuRunner(machine=machine, rootfs=rootfs, tmpdir=tmpdir,
35 deploy_dir_image=dir_image, display=display, 38 deploy_dir_image=dir_image, display=display,
36 logfile=bootlog, boottime=boottime, 39 logfile=bootlog, boottime=boottime,
37 use_kvm=kvm, use_slirp=slirp, dump_dir=dump_dir, 40 use_kvm=kvm, use_slirp=slirp, dump_dir=dump_dir,
38 dump_host_cmds=dump_host_cmds, logger=logger, 41 dump_host_cmds=dump_host_cmds, logger=logger,
39 serial_ports=serial_ports) 42 serial_ports=serial_ports, boot_patterns = boot_patterns)
40 43
41 def start(self, params=None, extra_bootparams=None, runqemuparams=''): 44 def start(self, params=None, extra_bootparams=None, runqemuparams=''):
42 if self.use_slirp and not self.server_ip: 45 if self.use_slirp and not self.server_ip:
diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py
index 4704422211..ed74ea8fad 100644
--- a/meta/lib/oeqa/utils/qemurunner.py
+++ b/meta/lib/oeqa/utils/qemurunner.py
@@ -21,6 +21,7 @@ import threading
21import codecs 21import codecs
22import logging 22import logging
23from oeqa.utils.dump import HostDumper 23from oeqa.utils.dump import HostDumper
24from collections import defaultdict
24 25
25# Get Unicode non printable control chars 26# Get Unicode non printable control chars
26control_range = list(range(0,32))+list(range(127,160)) 27control_range = list(range(0,32))+list(range(127,160))
@@ -31,7 +32,7 @@ re_control_char = re.compile('[%s]' % re.escape("".join(control_chars)))
31class QemuRunner: 32class QemuRunner:
32 33
33 def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, dump_host_cmds, 34 def __init__(self, machine, rootfs, display, tmpdir, deploy_dir_image, logfile, boottime, dump_dir, dump_host_cmds,
34 use_kvm, logger, use_slirp=False, serial_ports=2): 35 use_kvm, logger, use_slirp=False, serial_ports=2, boot_patterns = defaultdict(str)):
35 36
36 # Popen object for runqemu 37 # Popen object for runqemu
37 self.runqemu = None 38 self.runqemu = None
@@ -57,6 +58,7 @@ class QemuRunner:
57 self.use_slirp = use_slirp 58 self.use_slirp = use_slirp
58 self.serial_ports = serial_ports 59 self.serial_ports = serial_ports
59 self.msg = '' 60 self.msg = ''
61 self.boot_patterns = boot_patterns
60 62
61 self.runqemutime = 120 63 self.runqemutime = 120
62 self.qemu_pidfile = 'pidfile_'+str(os.getpid()) 64 self.qemu_pidfile = 'pidfile_'+str(os.getpid())
@@ -65,6 +67,25 @@ class QemuRunner:
65 67
66 self.logger = logger 68 self.logger = logger
67 69
70 # Enable testing other OS's
71 # Set commands for target communication, and default to Linux ALWAYS
72 # Other OS's or baremetal applications need to provide their
73 # own implementation passing it through QemuRunner's constructor
74 # or by passing them through TESTIMAGE_BOOT_PATTERNS[flag]
75 # provided variables, where <flag> is one of the mentioned below.
76 accepted_patterns = ['search_reached_prompt', 'send_login_user', 'search_login_succeeded', 'search_cmd_finished']
77 default_boot_patterns = defaultdict(str)
78 # Default to the usual paterns used to communicate with the target
79 default_boot_patterns['search_reached_prompt'] = b' login:'
80 default_boot_patterns['send_login_user'] = 'root\n'
81 default_boot_patterns['search_login_succeeded'] = r"root@[a-zA-Z0-9\-]+:~#"
82 default_boot_patterns['search_cmd_finished'] = r"[a-zA-Z0-9]+@[a-zA-Z0-9\-]+:~#"
83
84 # Only override patterns that were set e.g. login user TESTIMAGE_BOOT_PATTERNS[send_login_user] = "webserver\n"
85 for pattern in accepted_patterns:
86 if not self.boot_patterns[pattern]:
87 self.boot_patterns[pattern] = default_boot_patterns[pattern]
88
68 def create_socket(self): 89 def create_socket(self):
69 try: 90 try:
70 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 91 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -321,7 +342,7 @@ class QemuRunner:
321 self.log(data) 342 self.log(data)
322 343
323 data = b'' 344 data = b''
324 if b' login:' in bootlog: 345 if self.boot_patterns['search_reached_prompt'] in bootlog:
325 self.server_socket = qemusock 346 self.server_socket = qemusock
326 stopread = True 347 stopread = True
327 reachedlogin = True 348 reachedlogin = True
@@ -353,8 +374,8 @@ class QemuRunner:
353 374
354 # If we are not able to login the tests can continue 375 # If we are not able to login the tests can continue
355 try: 376 try:
356 (status, output) = self.run_serial("root\n", raw=True) 377 (status, output) = self.run_serial(self.boot_patterns['send_login_user'], raw=True)
357 if re.search(r"root@[a-zA-Z0-9\-]+:~#", output): 378 if re.search(self.boot_patterns['search_login_succeeded'], output):
358 self.logged = True 379 self.logged = True
359 self.logger.debug("Logged as root in serial console") 380 self.logger.debug("Logged as root in serial console")
360 if netconf: 381 if netconf:
@@ -478,7 +499,7 @@ class QemuRunner:
478 if answer: 499 if answer:
479 data += answer.decode('utf-8') 500 data += answer.decode('utf-8')
480 # Search the prompt to stop 501 # Search the prompt to stop
481 if re.search(r"[a-zA-Z0-9]+@[a-zA-Z0-9\-]+:~#", data): 502 if re.search(self.boot_patterns['search_cmd_finished'], data):
482 break 503 break
483 else: 504 else:
484 raise Exception("No data on serial console socket") 505 raise Exception("No data on serial console socket")