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