blob: 24669f461dabc6faf2c2ebbdba91c21d61285041 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# Copyright (C) 2013 Intel Corporation
2#
3# Released under the MIT license (see COPYING.MIT)
4
5# This module is used by testimage.bbclass for setting up and controlling a target machine.
6
7import os
8import shutil
9import subprocess
10import bb
11import traceback
12import sys
13import logging
14from oeqa.utils.sshcontrol import SSHControl
15from oeqa.utils.qemurunner import QemuRunner
16from oeqa.utils.qemutinyrunner import QemuTinyRunner
17from oeqa.utils.dump import TargetDumper
18from oeqa.controllers.testtargetloader import TestTargetLoader
19from abc import ABCMeta, abstractmethod
20
21def get_target_controller(d):
22 testtarget = d.getVar("TEST_TARGET", True)
23 # old, simple names
24 if testtarget == "qemu":
25 return QemuTarget(d)
26 elif testtarget == "simpleremote":
27 return SimpleRemoteTarget(d)
28 else:
29 # use the class name
30 try:
31 # is it a core class defined here?
32 controller = getattr(sys.modules[__name__], testtarget)
33 except AttributeError:
34 # nope, perhaps a layer defined one
35 try:
36 bbpath = d.getVar("BBPATH", True).split(':')
37 testtargetloader = TestTargetLoader()
38 controller = testtargetloader.get_controller_module(testtarget, bbpath)
39 except ImportError as e:
40 bb.fatal("Failed to import {0} from available controller modules:\n{1}".format(testtarget,traceback.format_exc()))
41 except AttributeError as e:
42 bb.fatal("Invalid TEST_TARGET - " + str(e))
43 return controller(d)
44
45
Patrick Williamsc0f7c042017-02-23 20:41:17 -060046class BaseTarget(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050047
48 supported_image_fstypes = []
49
50 def __init__(self, d):
51 self.connection = None
52 self.ip = None
53 self.server_ip = None
54 self.datetime = d.getVar('DATETIME', True)
55 self.testdir = d.getVar("TEST_LOG_DIR", True)
56 self.pn = d.getVar("PN", True)
57
58 @abstractmethod
59 def deploy(self):
60
61 self.sshlog = os.path.join(self.testdir, "ssh_target_log.%s" % self.datetime)
62 sshloglink = os.path.join(self.testdir, "ssh_target_log")
63 if os.path.islink(sshloglink):
64 os.unlink(sshloglink)
65 os.symlink(self.sshlog, sshloglink)
66 bb.note("SSH log file: %s" % self.sshlog)
67
68 @abstractmethod
Patrick Williamsc0f7c042017-02-23 20:41:17 -060069 def start(self, params=None, ssh=True, extra_bootparams=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050070 pass
71
72 @abstractmethod
73 def stop(self):
74 pass
75
76 @classmethod
77 def get_extra_files(self):
78 return None
79
80 @classmethod
81 def match_image_fstype(self, d, image_fstypes=None):
82 if not image_fstypes:
83 image_fstypes = d.getVar('IMAGE_FSTYPES', True).split(' ')
84 possible_image_fstypes = [fstype for fstype in self.supported_image_fstypes if fstype in image_fstypes]
85 if possible_image_fstypes:
86 return possible_image_fstypes[0]
87 else:
88 return None
89
90 def get_image_fstype(self, d):
91 image_fstype = self.match_image_fstype(d)
92 if image_fstype:
93 return image_fstype
94 else:
95 bb.fatal("IMAGE_FSTYPES should contain a Target Controller supported image fstype: %s " % ', '.join(map(str, self.supported_image_fstypes)))
96
97 def restart(self, params=None):
98 self.stop()
99 self.start(params)
100
101 def run(self, cmd, timeout=None):
102 return self.connection.run(cmd, timeout)
103
104 def copy_to(self, localpath, remotepath):
105 return self.connection.copy_to(localpath, remotepath)
106
107 def copy_from(self, remotepath, localpath):
108 return self.connection.copy_from(remotepath, localpath)
109
110
111
112class QemuTarget(BaseTarget):
113
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500114 supported_image_fstypes = ['ext3', 'ext4', 'cpio.gz', 'wic']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500115
116 def __init__(self, d):
117
118 super(QemuTarget, self).__init__(d)
119
120 self.image_fstype = self.get_image_fstype(d)
121 self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600122 self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.' + self.image_fstype)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123 self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin')
124 dump_target_cmds = d.getVar("testimage_dump_target", True)
125 dump_host_cmds = d.getVar("testimage_dump_host", True)
126 dump_dir = d.getVar("TESTIMAGE_DUMP_DIR", True)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600127 if d.getVar("QEMU_USE_KVM", False) is not None \
128 and d.getVar("QEMU_USE_KVM", False) == "True" \
129 and "x86" in d.getVar("MACHINE", True):
130 use_kvm = True
131 else:
132 use_kvm = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500133
134 # Log QemuRunner log output to a file
135 import oe.path
136 bb.utils.mkdirhier(self.testdir)
137 self.qemurunnerlog = os.path.join(self.testdir, 'qemurunner_log.%s' % self.datetime)
138 logger = logging.getLogger('BitBake.QemuRunner')
139 loggerhandler = logging.FileHandler(self.qemurunnerlog)
140 loggerhandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
141 logger.addHandler(loggerhandler)
142 oe.path.symlink(os.path.basename(self.qemurunnerlog), os.path.join(self.testdir, 'qemurunner_log'), force=True)
143
144 if d.getVar("DISTRO", True) == "poky-tiny":
145 self.runner = QemuTinyRunner(machine=d.getVar("MACHINE", True),
146 rootfs=self.rootfs,
147 tmpdir = d.getVar("TMPDIR", True),
148 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE", True),
149 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
150 logfile = self.qemulog,
151 kernel = self.kernel,
152 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True)))
153 else:
154 self.runner = QemuRunner(machine=d.getVar("MACHINE", True),
155 rootfs=self.rootfs,
156 tmpdir = d.getVar("TMPDIR", True),
157 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE", True),
158 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
159 logfile = self.qemulog,
160 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True)),
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600161 use_kvm = use_kvm,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162 dump_dir = dump_dir,
163 dump_host_cmds = d.getVar("testimage_dump_host", True))
164
165 self.target_dumper = TargetDumper(dump_target_cmds, dump_dir, self.runner)
166
167 def deploy(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600168 bb.utils.mkdirhier(self.testdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500169
170 qemuloglink = os.path.join(self.testdir, "qemu_boot_log")
171 if os.path.islink(qemuloglink):
172 os.unlink(qemuloglink)
173 os.symlink(self.qemulog, qemuloglink)
174
175 bb.note("rootfs file: %s" % self.rootfs)
176 bb.note("Qemu log file: %s" % self.qemulog)
177 super(QemuTarget, self).deploy()
178
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600179 def start(self, params=None, ssh=True, extra_bootparams=None):
180 if self.runner.start(params, get_ip=ssh, extra_bootparams=extra_bootparams):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500181 if ssh:
182 self.ip = self.runner.ip
183 self.server_ip = self.runner.server_ip
184 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500185 else:
186 self.stop()
187 if os.path.exists(self.qemulog):
188 with open(self.qemulog, 'r') as f:
189 bb.error("Qemu log output from %s:\n%s" % (self.qemulog, f.read()))
190 raise bb.build.FuncFailed("%s - FAILED to start qemu - check the task log and the boot log" % self.pn)
191
192 def check(self):
193 return self.runner.is_alive()
194
195 def stop(self):
196 self.runner.stop()
197 self.connection = None
198 self.ip = None
199 self.server_ip = None
200
201 def restart(self, params=None):
202 if self.runner.restart(params):
203 self.ip = self.runner.ip
204 self.server_ip = self.runner.server_ip
205 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
206 else:
207 raise bb.build.FuncFailed("%s - FAILED to re-start qemu - check the task log and the boot log" % self.pn)
208
209 def run_serial(self, command):
210 return self.runner.run_serial(command)
211
212
213class SimpleRemoteTarget(BaseTarget):
214
215 def __init__(self, d):
216 super(SimpleRemoteTarget, self).__init__(d)
217 addr = d.getVar("TEST_TARGET_IP", True) or bb.fatal('Please set TEST_TARGET_IP with the IP address of the machine you want to run the tests on.')
218 self.ip = addr.split(":")[0]
219 try:
220 self.port = addr.split(":")[1]
221 except IndexError:
222 self.port = None
223 bb.note("Target IP: %s" % self.ip)
224 self.server_ip = d.getVar("TEST_SERVER_IP", True)
225 if not self.server_ip:
226 try:
227 self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1]
228 except Exception as e:
229 bb.fatal("Failed to determine the host IP address (alternatively you can set TEST_SERVER_IP with the IP address of this machine): %s" % e)
230 bb.note("Server IP: %s" % self.server_ip)
231
232 def deploy(self):
233 super(SimpleRemoteTarget, self).deploy()
234
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600235 def start(self, params=None, ssh=True, extra_bootparams=None):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500236 if ssh:
237 self.connection = SSHControl(self.ip, logfile=self.sshlog, port=self.port)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500238
239 def stop(self):
240 self.connection = None
241 self.ip = None
242 self.server_ip = None