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, boot_patterns=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 boot_patterns = boot_patterns) 146 147 self.monitor_dumper = MonitorDumper(dump_monitor_cmds, dump_dir, self.runner) 148 if (self.monitor_dumper): 149 self.monitor_dumper.create_dir("qmp") 150 151 def deploy(self): 152 bb.utils.mkdirhier(self.testdir) 153 154 qemuloglink = os.path.join(self.testdir, "qemu_boot_log") 155 if os.path.islink(qemuloglink): 156 os.unlink(qemuloglink) 157 os.symlink(self.qemulog, qemuloglink) 158 159 self.logger.info("rootfs file: %s" % self.rootfs) 160 self.logger.info("Qemu log file: %s" % self.qemulog) 161 super(QemuTarget, self).deploy() 162 163 def start(self, params=None, ssh=True, extra_bootparams='', runqemuparams='', launch_cmd='', discard_writes=True): 164 if launch_cmd: 165 start = self.runner.launch(get_ip=ssh, launch_cmd=launch_cmd, qemuparams=params) 166 else: 167 start = self.runner.start(params, get_ip=ssh, extra_bootparams=extra_bootparams, runqemuparams=runqemuparams, discard_writes=discard_writes) 168 169 if start: 170 if ssh: 171 self.ip = self.runner.ip 172 self.server_ip = self.runner.server_ip 173 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog) 174 else: 175 self.stop() 176 if os.path.exists(self.qemulog): 177 with open(self.qemulog, 'r') as f: 178 bb.error("Qemu log output from %s:\n%s" % (self.qemulog, f.read())) 179 raise RuntimeError("%s - FAILED to start qemu - check the task log and the boot log" % self.pn) 180 181 def check(self): 182 return self.runner.is_alive() 183 184 def stop(self): 185 try: 186 self.runner.stop() 187 except: 188 pass 189 self.logger.removeHandler(self.loggerhandler) 190 self.loggerhandler.close() 191 self.connection = None 192 self.ip = None 193 self.server_ip = None 194 195 def restart(self, params=None): 196 if self.runner.restart(params): 197 self.ip = self.runner.ip 198 self.server_ip = self.runner.server_ip 199 self.connection = SSHControl(ip=self.ip, logfile=self.sshlog) 200 else: 201 raise RuntimeError("%s - FAILED to re-start qemu - check the task log and the boot log" % self.pn) 202 203 def run_serial(self, command, timeout=60): 204 return self.runner.run_serial(command, timeout=timeout) 205 206 207class SimpleRemoteTarget(BaseTarget): 208 209 def __init__(self, d): 210 super(SimpleRemoteTarget, self).__init__(d) 211 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.') 212 self.ip = addr.split(":")[0] 213 try: 214 self.port = addr.split(":")[1] 215 except IndexError: 216 self.port = None 217 self.logger.info("Target IP: %s" % self.ip) 218 self.server_ip = d.getVar("TEST_SERVER_IP") 219 if not self.server_ip: 220 try: 221 self.server_ip = subprocess.check_output(['ip', 'route', 'get', self.ip ]).split("\n")[0].split()[-1] 222 except Exception as e: 223 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) 224 self.logger.info("Server IP: %s" % self.server_ip) 225 226 def deploy(self): 227 super(SimpleRemoteTarget, self).deploy() 228 229 def start(self, params=None, ssh=True, extra_bootparams=None): 230 if ssh: 231 self.connection = SSHControl(self.ip, logfile=self.sshlog, port=self.port) 232 233 def stop(self): 234 self.connection = None 235 self.ip = None 236 self.server_ip = None 237