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