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