blob: 3255e3a5c635ce1d8cecf36241855bb8065576b7 [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
Brad Bishop6e60e8b2018-02-01 10:27:11 -050021logger = logging.getLogger('BitBake.QemuRunner')
22
Patrick Williamsc124f4f2015-09-15 14:41:29 -050023def get_target_controller(d):
Brad Bishop6e60e8b2018-02-01 10:27:11 -050024 testtarget = d.getVar("TEST_TARGET")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025 # old, simple names
26 if testtarget == "qemu":
27 return QemuTarget(d)
28 elif testtarget == "simpleremote":
29 return SimpleRemoteTarget(d)
30 else:
31 # use the class name
32 try:
33 # is it a core class defined here?
34 controller = getattr(sys.modules[__name__], testtarget)
35 except AttributeError:
36 # nope, perhaps a layer defined one
37 try:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050038 bbpath = d.getVar("BBPATH").split(':')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050039 testtargetloader = TestTargetLoader()
40 controller = testtargetloader.get_controller_module(testtarget, bbpath)
41 except ImportError as e:
42 bb.fatal("Failed to import {0} from available controller modules:\n{1}".format(testtarget,traceback.format_exc()))
43 except AttributeError as e:
44 bb.fatal("Invalid TEST_TARGET - " + str(e))
45 return controller(d)
46
47
Patrick Williamsc0f7c042017-02-23 20:41:17 -060048class BaseTarget(object, metaclass=ABCMeta):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050049
50 supported_image_fstypes = []
51
52 def __init__(self, d):
53 self.connection = None
54 self.ip = None
55 self.server_ip = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -050056 self.datetime = d.getVar('DATETIME')
57 self.testdir = d.getVar("TEST_LOG_DIR")
58 self.pn = d.getVar("PN")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050059
60 @abstractmethod
61 def deploy(self):
62
63 self.sshlog = os.path.join(self.testdir, "ssh_target_log.%s" % self.datetime)
64 sshloglink = os.path.join(self.testdir, "ssh_target_log")
65 if os.path.islink(sshloglink):
66 os.unlink(sshloglink)
67 os.symlink(self.sshlog, sshloglink)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050068 logger.info("SSH log file: %s" % self.sshlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050069
70 @abstractmethod
Patrick Williamsc0f7c042017-02-23 20:41:17 -060071 def start(self, params=None, ssh=True, extra_bootparams=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050072 pass
73
74 @abstractmethod
75 def stop(self):
76 pass
77
78 @classmethod
79 def get_extra_files(self):
80 return None
81
82 @classmethod
83 def match_image_fstype(self, d, image_fstypes=None):
84 if not image_fstypes:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050085 image_fstypes = d.getVar('IMAGE_FSTYPES').split(' ')
Patrick Williamsc124f4f2015-09-15 14:41:29 -050086 possible_image_fstypes = [fstype for fstype in self.supported_image_fstypes if fstype in image_fstypes]
87 if possible_image_fstypes:
88 return possible_image_fstypes[0]
89 else:
90 return None
91
92 def get_image_fstype(self, d):
93 image_fstype = self.match_image_fstype(d)
94 if image_fstype:
95 return image_fstype
96 else:
97 bb.fatal("IMAGE_FSTYPES should contain a Target Controller supported image fstype: %s " % ', '.join(map(str, self.supported_image_fstypes)))
98
99 def restart(self, params=None):
100 self.stop()
101 self.start(params)
102
103 def run(self, cmd, timeout=None):
104 return self.connection.run(cmd, timeout)
105
106 def copy_to(self, localpath, remotepath):
107 return self.connection.copy_to(localpath, remotepath)
108
109 def copy_from(self, remotepath, localpath):
110 return self.connection.copy_from(remotepath, localpath)
111
112
113
114class QemuTarget(BaseTarget):
115
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500116 supported_image_fstypes = ['ext3', 'ext4', 'cpio.gz', 'wic']
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500117
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500118 def __init__(self, d, image_fstype=None):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500119
120 super(QemuTarget, self).__init__(d)
121
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500122 self.rootfs = ''
123 self.kernel = ''
124 self.image_fstype = ''
125
126 if d.getVar('FIND_ROOTFS') == '1':
127 self.image_fstype = image_fstype or self.get_image_fstype(d)
128 self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("IMAGE_LINK_NAME") + '.' + self.image_fstype)
129 self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500130 self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500131 dump_target_cmds = d.getVar("testimage_dump_target")
132 dump_host_cmds = d.getVar("testimage_dump_host")
133 dump_dir = d.getVar("TESTIMAGE_DUMP_DIR")
134 qemu_use_kvm = d.getVar("QEMU_USE_KVM")
135 if qemu_use_kvm and \
136 (qemu_use_kvm == "True" and "x86" in d.getVar("MACHINE") or \
137 d.getVar("MACHINE") in qemu_use_kvm.split()):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600138 use_kvm = True
139 else:
140 use_kvm = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500141
142 # Log QemuRunner log output to a file
143 import oe.path
144 bb.utils.mkdirhier(self.testdir)
145 self.qemurunnerlog = os.path.join(self.testdir, 'qemurunner_log.%s' % self.datetime)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500146 loggerhandler = logging.FileHandler(self.qemurunnerlog)
147 loggerhandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
148 logger.addHandler(loggerhandler)
149 oe.path.symlink(os.path.basename(self.qemurunnerlog), os.path.join(self.testdir, 'qemurunner_log'), force=True)
150
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500151 if d.getVar("DISTRO") == "poky-tiny":
152 self.runner = QemuTinyRunner(machine=d.getVar("MACHINE"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500153 rootfs=self.rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500154 tmpdir = d.getVar("TMPDIR"),
155 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE"),
156 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500157 logfile = self.qemulog,
158 kernel = self.kernel,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500159 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT")))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500160 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500161 self.runner = QemuRunner(machine=d.getVar("MACHINE"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500162 rootfs=self.rootfs,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500163 tmpdir = d.getVar("TMPDIR"),
164 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE"),
165 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY"),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500166 logfile = self.qemulog,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500167 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT")),
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600168 use_kvm = use_kvm,
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500169 dump_dir = dump_dir,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500170 dump_host_cmds = d.getVar("testimage_dump_host"))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500171
172 self.target_dumper = TargetDumper(dump_target_cmds, dump_dir, self.runner)
173
174 def deploy(self):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600175 bb.utils.mkdirhier(self.testdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500176
177 qemuloglink = os.path.join(self.testdir, "qemu_boot_log")
178 if os.path.islink(qemuloglink):
179 os.unlink(qemuloglink)
180 os.symlink(self.qemulog, qemuloglink)
181
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500182 logger.info("rootfs file: %s" % self.rootfs)
183 logger.info("Qemu log file: %s" % self.qemulog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500184 super(QemuTarget, self).deploy()
185
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500186 def start(self, params=None, ssh=True, extra_bootparams='', runqemuparams='', launch_cmd='', discard_writes=True):
187 if launch_cmd:
188 start = self.runner.launch(get_ip=ssh, launch_cmd=launch_cmd)
189 else:
190 start = self.runner.start(params, get_ip=ssh, extra_bootparams=extra_bootparams, runqemuparams=runqemuparams, discard_writes=discard_writes)
191
192 if start:
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500193 if ssh:
194 self.ip = self.runner.ip
195 self.server_ip = self.runner.server_ip
196 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 else:
198 self.stop()
199 if os.path.exists(self.qemulog):
200 with open(self.qemulog, 'r') as f:
201 bb.error("Qemu log output from %s:\n%s" % (self.qemulog, f.read()))
202 raise bb.build.FuncFailed("%s - FAILED to start qemu - check the task log and the boot log" % self.pn)
203
204 def check(self):
205 return self.runner.is_alive()
206
207 def stop(self):
208 self.runner.stop()
209 self.connection = None
210 self.ip = None
211 self.server_ip = None
212
213 def restart(self, params=None):
214 if self.runner.restart(params):
215 self.ip = self.runner.ip
216 self.server_ip = self.runner.server_ip
217 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
218 else:
219 raise bb.build.FuncFailed("%s - FAILED to re-start qemu - check the task log and the boot log" % self.pn)
220
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500221 def run_serial(self, command, timeout=5):
222 return self.runner.run_serial(command, timeout=timeout)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500223
224
225class SimpleRemoteTarget(BaseTarget):
226
227 def __init__(self, d):
228 super(SimpleRemoteTarget, self).__init__(d)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500229 addr = d.getVar("TEST_TARGET_IP") or bb.fatal('Please set TEST_TARGET_IP with the IP address of the machine you want to run the tests on.')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500230 self.ip = addr.split(":")[0]
231 try:
232 self.port = addr.split(":")[1]
233 except IndexError:
234 self.port = None
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500235 logger.info("Target IP: %s" % self.ip)
236 self.server_ip = d.getVar("TEST_SERVER_IP")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500237 if not self.server_ip:
238 try:
239 self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1]
240 except Exception as e:
241 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)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500242 logger.info("Server IP: %s" % self.server_ip)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500243
244 def deploy(self):
245 super(SimpleRemoteTarget, self).deploy()
246
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600247 def start(self, params=None, ssh=True, extra_bootparams=None):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500248 if ssh:
249 self.connection = SSHControl(self.ip, logfile=self.sshlog, port=self.port)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500250
251 def stop(self):
252 self.connection = None
253 self.ip = None
254 self.server_ip = None