blob: 5422a617c4cc4933c48f5189645d7931e4ea894e [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
46class BaseTarget(object):
47
48 __metaclass__ = ABCMeta
49
50 supported_image_fstypes = []
51
52 def __init__(self, d):
53 self.connection = None
54 self.ip = None
55 self.server_ip = None
56 self.datetime = d.getVar('DATETIME', True)
57 self.testdir = d.getVar("TEST_LOG_DIR", True)
58 self.pn = d.getVar("PN", True)
59
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)
68 bb.note("SSH log file: %s" % self.sshlog)
69
70 @abstractmethod
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050071 def start(self, params=None, ssh=True):
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:
85 image_fstypes = d.getVar('IMAGE_FSTYPES', True).split(' ')
86 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
118 def __init__(self, d):
119
120 super(QemuTarget, self).__init__(d)
121
122 self.image_fstype = self.get_image_fstype(d)
123 self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime)
124 self.origrootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("IMAGE_LINK_NAME", True) + '.' + self.image_fstype)
125 self.rootfs = os.path.join(self.testdir, d.getVar("IMAGE_LINK_NAME", True) + '-testimage.' + self.image_fstype)
126 self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE", True), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin')
127 dump_target_cmds = d.getVar("testimage_dump_target", True)
128 dump_host_cmds = d.getVar("testimage_dump_host", True)
129 dump_dir = d.getVar("TESTIMAGE_DUMP_DIR", True)
130
131 # Log QemuRunner log output to a file
132 import oe.path
133 bb.utils.mkdirhier(self.testdir)
134 self.qemurunnerlog = os.path.join(self.testdir, 'qemurunner_log.%s' % self.datetime)
135 logger = logging.getLogger('BitBake.QemuRunner')
136 loggerhandler = logging.FileHandler(self.qemurunnerlog)
137 loggerhandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
138 logger.addHandler(loggerhandler)
139 oe.path.symlink(os.path.basename(self.qemurunnerlog), os.path.join(self.testdir, 'qemurunner_log'), force=True)
140
141 if d.getVar("DISTRO", True) == "poky-tiny":
142 self.runner = QemuTinyRunner(machine=d.getVar("MACHINE", True),
143 rootfs=self.rootfs,
144 tmpdir = d.getVar("TMPDIR", True),
145 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE", True),
146 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
147 logfile = self.qemulog,
148 kernel = self.kernel,
149 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True)))
150 else:
151 self.runner = QemuRunner(machine=d.getVar("MACHINE", True),
152 rootfs=self.rootfs,
153 tmpdir = d.getVar("TMPDIR", True),
154 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE", True),
155 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY", True),
156 logfile = self.qemulog,
157 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT", True)),
158 dump_dir = dump_dir,
159 dump_host_cmds = d.getVar("testimage_dump_host", True))
160
161 self.target_dumper = TargetDumper(dump_target_cmds, dump_dir, self.runner)
162
163 def deploy(self):
164 try:
165 bb.utils.mkdirhier(self.testdir)
166 shutil.copyfile(self.origrootfs, self.rootfs)
167 except Exception as e:
168 bb.fatal("Error copying rootfs: %s" % e)
169
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 Williamsd8c66bc2016-06-20 12:57:21 -0500179 def start(self, params=None, ssh=True):
180 if self.runner.start(params, get_ip=ssh):
181 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 Williamsd8c66bc2016-06-20 12:57:21 -0500235 def start(self, params=None, ssh=True):
236 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