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.controllers.testtargetloader import TestTargetLoader 21from abc import ABCMeta, abstractmethod 22 23class BaseTarget(object, metaclass=ABCMeta): 24 25 supported_image_fstypes = [] 26 27 def __init__(self, d, logger): 28 self.connection = None 29 self.ip = None 30 self.server_ip = None 31 self.datetime = d.getVar('DATETIME') 32 self.testdir = d.getVar("TEST_LOG_DIR") 33 self.pn = d.getVar("PN") 34 self.logger = logger 35 36 @abstractmethod 37 def deploy(self): 38 39 self.sshlog = os.path.join(self.testdir, "ssh_target_log.%s" % self.datetime) 40 sshloglink = os.path.join(self.testdir, "ssh_target_log") 41 if os.path.islink(sshloglink): 42 os.unlink(sshloglink) 43 os.symlink(self.sshlog, sshloglink) 44 self.logger.info("SSH log file: %s" % self.sshlog) 45 46 @abstractmethod 47 def start(self, params=None, ssh=True, extra_bootparams=None): 48 pass 49 50 @abstractmethod 51 def stop(self): 52 pass 53 54 @classmethod 55 def get_extra_files(self): 56 return None 57 58 @classmethod 59 def match_image_fstype(self, d, image_fstypes=None): 60 if not image_fstypes: 61 image_fstypes = d.getVar('IMAGE_FSTYPES').split(' ') 62 possible_image_fstypes = [fstype for fstype in self.supported_image_fstypes if fstype in image_fstypes] 63 if possible_image_fstypes: 64 return possible_image_fstypes[0] 65 else: 66 return None 67 68 def get_image_fstype(self, d): 69 image_fstype = self.match_image_fstype(d) 70 if image_fstype: 71 return image_fstype 72 else: 73 bb.fatal("IMAGE_FSTYPES should contain a Target Controller supported image fstype: %s " % ', '.join(map(str, self.supported_image_fstypes))) 74 75 def restart(self, params=None): 76 self.stop() 77 self.start(params) 78 79 def run(self, cmd, timeout=None): 80 return self.connection.run(cmd, timeout) 81 82 def copy_to(self, localpath, remotepath): 83 return self.connection.copy_to(localpath, remotepath) 84 85 def copy_from(self, remotepath, localpath): 86 return self.connection.copy_from(remotepath, localpath) 87 88 89 90class QemuTarget(BaseTarget): 91 92 supported_image_fstypes = ['ext3', 'ext4', 'cpio.gz', 'wic'] 93 94 def __init__(self, d, logger, image_fstype=None): 95 96 import oe.types 97 98 super(QemuTarget, self).__init__(d, logger) 99 100 self.rootfs = '' 101 self.kernel = '' 102 self.image_fstype = '' 103 104 if d.getVar('FIND_ROOTFS') == '1': 105 self.image_fstype = image_fstype or self.get_image_fstype(d) 106 self.rootfs = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("IMAGE_LINK_NAME") + '.' + self.image_fstype) 107 self.kernel = os.path.join(d.getVar("DEPLOY_DIR_IMAGE"), d.getVar("KERNEL_IMAGETYPE", False) + '-' + d.getVar('MACHINE', False) + '.bin') 108 self.qemulog = os.path.join(self.testdir, "qemu_boot_log.%s" % self.datetime) 109 dump_target_cmds = d.getVar("testimage_dump_target") 110 dump_host_cmds = d.getVar("testimage_dump_host") 111 dump_dir = d.getVar("TESTIMAGE_DUMP_DIR") 112 if not dump_dir: 113 dump_dir = os.path.join(d.getVar('LOG_DIR'), 'runtime-hostdump') 114 use_kvm = oe.types.qemu_use_kvm(d.getVar('QEMU_USE_KVM'), d.getVar('TARGET_ARCH')) 115 116 # Log QemuRunner log output to a file 117 import oe.path 118 bb.utils.mkdirhier(self.testdir) 119 self.qemurunnerlog = os.path.join(self.testdir, 'qemurunner_log.%s' % self.datetime) 120 self.loggerhandler = logging.FileHandler(self.qemurunnerlog) 121 self.loggerhandler.setFormatter(logging.Formatter("%(levelname)s: %(message)s")) 122 self.logger.addHandler(self.loggerhandler) 123 oe.path.symlink(os.path.basename(self.qemurunnerlog), os.path.join(self.testdir, 'qemurunner_log'), force=True) 124 125 if d.getVar("DISTRO") == "poky-tiny": 126 self.runner = QemuTinyRunner(machine=d.getVar("MACHINE"), 127 rootfs=self.rootfs, 128 tmpdir = d.getVar("TMPDIR"), 129 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE"), 130 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY"), 131 logfile = self.qemulog, 132 kernel = self.kernel, 133 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT")), 134 logger = logger) 135 else: 136 self.runner = QemuRunner(machine=d.getVar("MACHINE"), 137 rootfs=self.rootfs, 138 tmpdir = d.getVar("TMPDIR"), 139 deploy_dir_image = d.getVar("DEPLOY_DIR_IMAGE"), 140 display = d.getVar("BB_ORIGENV", False).getVar("DISPLAY"), 141 logfile = self.qemulog, 142 boottime = int(d.getVar("TEST_QEMUBOOT_TIMEOUT")), 143 use_kvm = use_kvm, 144 dump_dir = dump_dir, 145 dump_host_cmds = d.getVar("testimage_dump_host"), 146 logger = logger, 147 serial_ports = len(d.getVar("SERIAL_CONSOLES").split())) 148 149 self.target_dumper = TargetDumper(dump_target_cmds, dump_dir, self.runner) 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.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 RuntimError("%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