blob: edc0d01c1edafad4079e9988acebb04b99f5c204 [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
71 def start(self, params=None):
72 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
116 supported_image_fstypes = ['ext3', 'ext4', 'cpio.gz']
117
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
179 def start(self, params=None):
180 if self.runner.start(params):
181 self.ip = self.runner.ip
182 self.server_ip = self.runner.server_ip
183 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
184 else:
185 self.stop()
186 if os.path.exists(self.qemulog):
187 with open(self.qemulog, 'r') as f:
188 bb.error("Qemu log output from %s:\n%s" % (self.qemulog, f.read()))
189 raise bb.build.FuncFailed("%s - FAILED to start qemu - check the task log and the boot log" % self.pn)
190
191 def check(self):
192 return self.runner.is_alive()
193
194 def stop(self):
195 self.runner.stop()
196 self.connection = None
197 self.ip = None
198 self.server_ip = None
199
200 def restart(self, params=None):
201 if self.runner.restart(params):
202 self.ip = self.runner.ip
203 self.server_ip = self.runner.server_ip
204 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
205 else:
206 raise bb.build.FuncFailed("%s - FAILED to re-start qemu - check the task log and the boot log" % self.pn)
207
208 def run_serial(self, command):
209 return self.runner.run_serial(command)
210
211
212class SimpleRemoteTarget(BaseTarget):
213
214 def __init__(self, d):
215 super(SimpleRemoteTarget, self).__init__(d)
216 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.')
217 self.ip = addr.split(":")[0]
218 try:
219 self.port = addr.split(":")[1]
220 except IndexError:
221 self.port = None
222 bb.note("Target IP: %s" % self.ip)
223 self.server_ip = d.getVar("TEST_SERVER_IP", True)
224 if not self.server_ip:
225 try:
226 self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1]
227 except Exception as e:
228 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)
229 bb.note("Server IP: %s" % self.server_ip)
230
231 def deploy(self):
232 super(SimpleRemoteTarget, self).deploy()
233
234 def start(self, params=None):
235 self.connection = SSHControl(self.ip, logfile=self.sshlog, port=self.port)
236
237 def stop(self):
238 self.connection = None
239 self.ip = None
240 self.server_ip = None