xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oeqa/targetcontrol.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1#
2# Copyright (C) 2013 Intel Corporation
3#
4# SPDX-License-Identifier: MIT
5#
6
7# This module is used by testimage.bbclass for setting up and controlling a target machine.
8
9import os
10import shutil
11import subprocess
12import bb
13import traceback
14import sys
15import logging
16from oeqa.utils.sshcontrol import SSHControl
17from oeqa.utils.qemurunner import QemuRunner
18from oeqa.utils.qemutinyrunner import QemuTinyRunner
19from oeqa.utils.dump import TargetDumper
20from oeqa.utils.dump import MonitorDumper
21from oeqa.controllers.testtargetloader import TestTargetLoader
22from abc import ABCMeta, abstractmethod
23
24class BaseTarget(object, metaclass=ABCMeta):
25
26    supported_image_fstypes = []
27
28    def __init__(self, d, logger):
29        self.connection = None
30        self.ip = None
31        self.server_ip = None
32        self.datetime = d.getVar('DATETIME')
33        self.testdir = d.getVar("TEST_LOG_DIR")
34        self.pn = d.getVar("PN")
35        self.logger = logger
36
37    @abstractmethod
38    def deploy(self):
39
40        self.sshlog = os.path.join(self.testdir, "ssh_target_log.%s" % self.datetime)
41        sshloglink = os.path.join(self.testdir, "ssh_target_log")
42        if os.path.islink(sshloglink):
43            os.unlink(sshloglink)
44        os.symlink(self.sshlog, sshloglink)
45        self.logger.info("SSH log file: %s" %  self.sshlog)
46
47    @abstractmethod
48    def start(self, params=None, ssh=True, extra_bootparams=None):
49        pass
50
51    @abstractmethod
52    def stop(self):
53        pass
54
55    @classmethod
56    def get_extra_files(self):
57        return None
58
59    @classmethod
60    def match_image_fstype(self, d, image_fstypes=None):
61        if not image_fstypes:
62            image_fstypes = d.getVar('IMAGE_FSTYPES').split(' ')
63        possible_image_fstypes = [fstype for fstype in self.supported_image_fstypes if fstype in image_fstypes]
64        if possible_image_fstypes:
65            return possible_image_fstypes[0]
66        else:
67            return None
68
69    def get_image_fstype(self, d):
70        image_fstype = self.match_image_fstype(d)
71        if image_fstype:
72            return image_fstype
73        else:
74            bb.fatal("IMAGE_FSTYPES should contain a Target Controller supported image fstype: %s " % ', '.join(map(str, self.supported_image_fstypes)))
75
76    def restart(self, params=None):
77        self.stop()
78        self.start(params)
79
80    def run(self, cmd, timeout=None):
81        return self.connection.run(cmd, timeout)
82
83    def copy_to(self, localpath, remotepath):
84        return self.connection.copy_to(localpath, remotepath)
85
86    def copy_from(self, remotepath, localpath):
87        return self.connection.copy_from(remotepath, localpath)
88
89
90
91class QemuTarget(BaseTarget):
92
93    supported_image_fstypes = ['ext3', 'ext4', 'cpio.gz', 'wic']
94
95    def __init__(self, d, logger, image_fstype=None):
96
97        import oe.types
98
99        super(QemuTarget, self).__init__(d, logger)
100
101        self.rootfs = ''
102        self.kernel = ''
103        self.image_fstype = ''
104
105        if d.getVar('FIND_ROOTFS') == '1':
106            self.image_fstype = image_fstype or self.get_image_fstype(d)
107            self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"),  d.getVar("IMAGE_LINK_NAME") + '.' + self.image_fstype)
108            self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin')
109        self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime)
110        dump_target_cmds = d.getVar("testimage_dump_target")
111        dump_host_cmds = d.getVar("testimage_dump_host")
112        dump_monitor_cmds = d.getVar("testimage_dump_monitor")
113        dump_dir = d.getVar("TESTIMAGE_DUMP_DIR")
114        if not dump_dir:
115            dump_dir = os.path.join(d.getVar('LOG_DIR'), 'runtime-hostdump')
116        use_kvm = oe.types.qemu_use_kvm(d.getVar('QEMU_USE_KVM'), d.getVar('TARGET_ARCH'))
117
118        # Log QemuRunner log output to a file
119        import oe.path
120        bb.utils.mkdirhier(self.testdir)
121        self.qemurunnerlog = os.path.join(self.testdir, 'qemurunner_log.%s' % self.datetime)
122        self.loggerhandler = logging.FileHandler(self.qemurunnerlog)
123        self.loggerhandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
124        self.logger.addHandler(self.loggerhandler)
125        oe.path.symlink(os.path.basename(self.qemurunnerlog), os.path.join(self.testdir, 'qemurunner_log'), force=True)
126
127        if d.getVar("DISTRO") == "poky-tiny":
128            self.runner = QemuTinyRunner(machine=d.getVar("MACHINE"),
129                            rootfs=self.rootfs,
130                            tmpdir = d.getVar("TMPDIR"),
131                            deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE"),
132                            display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY"),
133                            logfile = self.qemulog,
134                            kernel = self.kernel,
135                            boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT")),
136                            tmpfsdir = d.getVar("RUNQEMU_TMPFS_DIR"),
137                            logger = logger)
138        else:
139            self.runner = QemuRunner(machine=d.getVar("MACHINE"),
140                            rootfs=self.rootfs,
141                            tmpdir = d.getVar("TMPDIR"),
142                            deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE"),
143                            display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY"),
144                            logfile = self.qemulog,
145                            boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT")),
146                            use_kvm = use_kvm,
147                            dump_dir = dump_dir,
148                            dump_host_cmds = d.getVar("testimage_dump_host"),
149                            logger = logger,
150                            tmpfsdir = d.getVar("RUNQEMU_TMPFS_DIR"),
151                            serial_ports = len(d.getVar("SERIAL_CONSOLES").split()))
152
153        self.target_dumper = TargetDumper(dump_target_cmds, dump_dir, self.runner)
154        self.monitor_dumper = MonitorDumper(dump_monitor_cmds, dump_dir, self.runner)
155        if (self.monitor_dumper):
156            self.monitor_dumper.create_dir("qmp")
157
158    def deploy(self):
159        bb.utils.mkdirhier(self.testdir)
160
161        qemuloglink = os.path.join(self.testdir, "qemu_boot_log")
162        if os.path.islink(qemuloglink):
163            os.unlink(qemuloglink)
164        os.symlink(self.qemulog, qemuloglink)
165
166        self.logger.info("rootfs file: %s" %  self.rootfs)
167        self.logger.info("Qemu log file: %s" % self.qemulog)
168        super(QemuTarget, self).deploy()
169
170    def start(self, params=None, ssh=True, extra_bootparams='', runqemuparams='', launch_cmd='', discard_writes=True):
171        if launch_cmd:
172            start = self.runner.launch(get_ip=ssh, launch_cmd=launch_cmd, qemuparams=params)
173        else:
174            start = self.runner.start(params, get_ip=ssh, extra_bootparams=extra_bootparams, runqemuparams=runqemuparams, discard_writes=discard_writes)
175
176        if start:
177            if ssh:
178                self.ip = self.runner.ip
179                self.server_ip = self.runner.server_ip
180                self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
181        else:
182            self.stop()
183            if os.path.exists(self.qemulog):
184                with open(self.qemulog, 'r') as f:
185                    bb.error("Qemu log output from %s:\n%s" % (self.qemulog, f.read()))
186            raise RuntimeError("%s - FAILED to start qemu - check the task log and the boot log" % self.pn)
187
188    def check(self):
189        return self.runner.is_alive()
190
191    def stop(self):
192        try:
193            self.runner.stop()
194        except:
195            pass
196        self.logger.removeHandler(self.loggerhandler)
197        self.loggerhandler.close()
198        self.connection = None
199        self.ip = None
200        self.server_ip = None
201
202    def restart(self, params=None):
203        if self.runner.restart(params):
204            self.ip = self.runner.ip
205            self.server_ip = self.runner.server_ip
206            self.connection = SSHControl(ip=self.ip, logfile=self.sshlog)
207        else:
208            raise RuntimError("%s - FAILED to re-start qemu - check the task log and the boot log" % self.pn)
209
210    def run_serial(self, command, timeout=60):
211        return self.runner.run_serial(command, timeout=timeout)
212
213
214class SimpleRemoteTarget(BaseTarget):
215
216    def __init__(self, d):
217        super(SimpleRemoteTarget, self).__init__(d)
218        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.')
219        self.ip = addr.split(":")[0]
220        try:
221            self.port = addr.split(":")[1]
222        except IndexError:
223            self.port = None
224        self.logger.info("Target IP: %s" % self.ip)
225        self.server_ip = d.getVar("TEST_SERVER_IP")
226        if not self.server_ip:
227            try:
228                self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1]
229            except Exception as e:
230                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)
231        self.logger.info("Server IP: %s" % self.server_ip)
232
233    def deploy(self):
234        super(SimpleRemoteTarget, self).deploy()
235
236    def start(self, params=None, ssh=True, extra_bootparams=None):
237        if ssh:
238            self.connection = SSHControl(self.ip, logfile=self.sshlog, port=self.port)
239
240    def stop(self):
241        self.connection = None
242        self.ip = None
243        self.server_ip = None
244