1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# Copyright (C) 2016 Intel Corporation 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# SPDX-License-Identifier: MIT 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun 7*4882a593Smuzhiyunimport os 8*4882a593Smuzhiyunimport sys 9*4882a593Smuzhiyun 10*4882a593Smuzhiyunfrom oeqa.core.context import OETestContext, OETestContextExecutor 11*4882a593Smuzhiyunfrom oeqa.core.target.ssh import OESSHTarget 12*4882a593Smuzhiyunfrom oeqa.core.target.qemu import OEQemuTarget 13*4882a593Smuzhiyunfrom oeqa.utils.dump import HostDumper 14*4882a593Smuzhiyun 15*4882a593Smuzhiyunfrom oeqa.runtime.loader import OERuntimeTestLoader 16*4882a593Smuzhiyun 17*4882a593Smuzhiyunclass OERuntimeTestContext(OETestContext): 18*4882a593Smuzhiyun loaderClass = OERuntimeTestLoader 19*4882a593Smuzhiyun runtime_files_dir = os.path.join( 20*4882a593Smuzhiyun os.path.dirname(os.path.abspath(__file__)), "files") 21*4882a593Smuzhiyun 22*4882a593Smuzhiyun def __init__(self, td, logger, target, 23*4882a593Smuzhiyun host_dumper, image_packages, extract_dir): 24*4882a593Smuzhiyun super(OERuntimeTestContext, self).__init__(td, logger) 25*4882a593Smuzhiyun 26*4882a593Smuzhiyun self.target = target 27*4882a593Smuzhiyun self.image_packages = image_packages 28*4882a593Smuzhiyun self.host_dumper = host_dumper 29*4882a593Smuzhiyun self.extract_dir = extract_dir 30*4882a593Smuzhiyun self._set_target_cmds() 31*4882a593Smuzhiyun 32*4882a593Smuzhiyun def _set_target_cmds(self): 33*4882a593Smuzhiyun self.target_cmds = {} 34*4882a593Smuzhiyun 35*4882a593Smuzhiyun self.target_cmds['ps'] = 'ps' 36*4882a593Smuzhiyun if 'procps' in self.image_packages: 37*4882a593Smuzhiyun self.target_cmds['ps'] = self.target_cmds['ps'] + ' -ef' 38*4882a593Smuzhiyun 39*4882a593Smuzhiyunclass OERuntimeTestContextExecutor(OETestContextExecutor): 40*4882a593Smuzhiyun _context_class = OERuntimeTestContext 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun name = 'runtime' 43*4882a593Smuzhiyun help = 'runtime test component' 44*4882a593Smuzhiyun description = 'executes runtime tests over targets' 45*4882a593Smuzhiyun 46*4882a593Smuzhiyun default_cases = os.path.join(os.path.abspath(os.path.dirname(__file__)), 47*4882a593Smuzhiyun 'cases') 48*4882a593Smuzhiyun default_data = None 49*4882a593Smuzhiyun default_test_data = 'data/testdata.json' 50*4882a593Smuzhiyun default_tests = '' 51*4882a593Smuzhiyun default_json_result_dir = '%s-results' % name 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun default_target_type = 'simpleremote' 54*4882a593Smuzhiyun default_manifest = 'data/manifest' 55*4882a593Smuzhiyun default_server_ip = '192.168.7.1' 56*4882a593Smuzhiyun default_target_ip = '192.168.7.2' 57*4882a593Smuzhiyun default_extract_dir = 'packages/extracted' 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun def register_commands(self, logger, subparsers): 60*4882a593Smuzhiyun super(OERuntimeTestContextExecutor, self).register_commands(logger, subparsers) 61*4882a593Smuzhiyun 62*4882a593Smuzhiyun runtime_group = self.parser.add_argument_group('runtime options') 63*4882a593Smuzhiyun 64*4882a593Smuzhiyun runtime_group.add_argument('--target-type', action='store', 65*4882a593Smuzhiyun default=self.default_target_type, choices=['simpleremote', 'qemu'], 66*4882a593Smuzhiyun help="Target type of device under test, default: %s" \ 67*4882a593Smuzhiyun % self.default_target_type) 68*4882a593Smuzhiyun runtime_group.add_argument('--target-ip', action='store', 69*4882a593Smuzhiyun default=self.default_target_ip, 70*4882a593Smuzhiyun help="IP address and optionally ssh port (default 22) of device under test, for example '192.168.0.7:22'. Default: %s" \ 71*4882a593Smuzhiyun % self.default_target_ip) 72*4882a593Smuzhiyun runtime_group.add_argument('--server-ip', action='store', 73*4882a593Smuzhiyun default=self.default_target_ip, 74*4882a593Smuzhiyun help="IP address of the test host from test target machine, default: %s" \ 75*4882a593Smuzhiyun % self.default_server_ip) 76*4882a593Smuzhiyun 77*4882a593Smuzhiyun runtime_group.add_argument('--host-dumper-dir', action='store', 78*4882a593Smuzhiyun help="Directory where host status is dumped, if tests fails") 79*4882a593Smuzhiyun 80*4882a593Smuzhiyun runtime_group.add_argument('--packages-manifest', action='store', 81*4882a593Smuzhiyun default=self.default_manifest, 82*4882a593Smuzhiyun help="Package manifest of the image under test, default: %s" \ 83*4882a593Smuzhiyun % self.default_manifest) 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun runtime_group.add_argument('--extract-dir', action='store', 86*4882a593Smuzhiyun default=self.default_extract_dir, 87*4882a593Smuzhiyun help='Directory where extracted packages reside, default: %s' \ 88*4882a593Smuzhiyun % self.default_extract_dir) 89*4882a593Smuzhiyun 90*4882a593Smuzhiyun runtime_group.add_argument('--qemu-boot', action='store', 91*4882a593Smuzhiyun help="Qemu boot configuration, only needed when target_type is QEMU.") 92*4882a593Smuzhiyun 93*4882a593Smuzhiyun @staticmethod 94*4882a593Smuzhiyun def getTarget(target_type, logger, target_ip, server_ip, **kwargs): 95*4882a593Smuzhiyun target = None 96*4882a593Smuzhiyun 97*4882a593Smuzhiyun if target_ip: 98*4882a593Smuzhiyun target_ip_port = target_ip.split(':') 99*4882a593Smuzhiyun if len(target_ip_port) == 2: 100*4882a593Smuzhiyun target_ip = target_ip_port[0] 101*4882a593Smuzhiyun kwargs['port'] = target_ip_port[1] 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun if server_ip: 104*4882a593Smuzhiyun server_ip_port = server_ip.split(':') 105*4882a593Smuzhiyun if len(server_ip_port) == 2: 106*4882a593Smuzhiyun server_ip = server_ip_port[0] 107*4882a593Smuzhiyun kwargs['server_port'] = int(server_ip_port[1]) 108*4882a593Smuzhiyun 109*4882a593Smuzhiyun if target_type == 'simpleremote': 110*4882a593Smuzhiyun target = OESSHTarget(logger, target_ip, server_ip, **kwargs) 111*4882a593Smuzhiyun elif target_type == 'qemu': 112*4882a593Smuzhiyun target = OEQemuTarget(logger, server_ip, **kwargs) 113*4882a593Smuzhiyun else: 114*4882a593Smuzhiyun # XXX: This code uses the old naming convention for controllers and 115*4882a593Smuzhiyun # targets, the idea it is to leave just targets as the controller 116*4882a593Smuzhiyun # most of the time was just a wrapper. 117*4882a593Smuzhiyun # XXX: This code tries to import modules from lib/oeqa/controllers 118*4882a593Smuzhiyun # directory and treat them as controllers, it will less error prone 119*4882a593Smuzhiyun # to use introspection to load such modules. 120*4882a593Smuzhiyun # XXX: Don't base your targets on this code it will be refactored 121*4882a593Smuzhiyun # in the near future. 122*4882a593Smuzhiyun # Custom target module loading 123*4882a593Smuzhiyun controller = OERuntimeTestContextExecutor.getControllerModule(target_type) 124*4882a593Smuzhiyun target = controller(logger, target_ip, server_ip, **kwargs) 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun return target 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun # Search oeqa.controllers module directory for and return a controller 129*4882a593Smuzhiyun # corresponding to the given target name. 130*4882a593Smuzhiyun # AttributeError raised if not found. 131*4882a593Smuzhiyun # ImportError raised if a provided module can not be imported. 132*4882a593Smuzhiyun @staticmethod 133*4882a593Smuzhiyun def getControllerModule(target): 134*4882a593Smuzhiyun controllerslist = OERuntimeTestContextExecutor._getControllerModulenames() 135*4882a593Smuzhiyun controller = OERuntimeTestContextExecutor._loadControllerFromName(target, controllerslist) 136*4882a593Smuzhiyun return controller 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun # Return a list of all python modules in lib/oeqa/controllers for each 139*4882a593Smuzhiyun # layer in bbpath 140*4882a593Smuzhiyun @staticmethod 141*4882a593Smuzhiyun def _getControllerModulenames(): 142*4882a593Smuzhiyun 143*4882a593Smuzhiyun controllerslist = [] 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun def add_controller_list(path): 146*4882a593Smuzhiyun if not os.path.exists(os.path.join(path, '__init__.py')): 147*4882a593Smuzhiyun raise OSError('Controllers directory %s exists but is missing __init__.py' % path) 148*4882a593Smuzhiyun files = sorted([f for f in os.listdir(path) if f.endswith('.py') and not f.startswith('_') and not f.startswith('.#')]) 149*4882a593Smuzhiyun for f in files: 150*4882a593Smuzhiyun module = 'oeqa.controllers.' + f[:-3] 151*4882a593Smuzhiyun if module not in controllerslist: 152*4882a593Smuzhiyun controllerslist.append(module) 153*4882a593Smuzhiyun else: 154*4882a593Smuzhiyun raise RuntimeError("Duplicate controller module found for %s. Layers should create unique controller module names" % module) 155*4882a593Smuzhiyun 156*4882a593Smuzhiyun # sys.path can contain duplicate paths, but because of the login in 157*4882a593Smuzhiyun # add_controller_list this doesn't work and causes testimage to abort. 158*4882a593Smuzhiyun # Remove duplicates using an intermediate dictionary to ensure this 159*4882a593Smuzhiyun # doesn't happen. 160*4882a593Smuzhiyun for p in list(dict.fromkeys(sys.path)): 161*4882a593Smuzhiyun controllerpath = os.path.join(p, 'oeqa', 'controllers') 162*4882a593Smuzhiyun if os.path.exists(controllerpath): 163*4882a593Smuzhiyun add_controller_list(controllerpath) 164*4882a593Smuzhiyun return controllerslist 165*4882a593Smuzhiyun 166*4882a593Smuzhiyun # Search for and return a controller from given target name and 167*4882a593Smuzhiyun # set of module names. 168*4882a593Smuzhiyun # Raise AttributeError if not found. 169*4882a593Smuzhiyun # Raise ImportError if a provided module can not be imported 170*4882a593Smuzhiyun @staticmethod 171*4882a593Smuzhiyun def _loadControllerFromName(target, modulenames): 172*4882a593Smuzhiyun for name in modulenames: 173*4882a593Smuzhiyun obj = OERuntimeTestContextExecutor._loadControllerFromModule(target, name) 174*4882a593Smuzhiyun if obj: 175*4882a593Smuzhiyun return obj 176*4882a593Smuzhiyun raise AttributeError("Unable to load {0} from available modules: {1}".format(target, str(modulenames))) 177*4882a593Smuzhiyun 178*4882a593Smuzhiyun # Search for and return a controller or None from given module name 179*4882a593Smuzhiyun @staticmethod 180*4882a593Smuzhiyun def _loadControllerFromModule(target, modulename): 181*4882a593Smuzhiyun try: 182*4882a593Smuzhiyun import importlib 183*4882a593Smuzhiyun module = importlib.import_module(modulename) 184*4882a593Smuzhiyun return getattr(module, target) 185*4882a593Smuzhiyun except AttributeError: 186*4882a593Smuzhiyun return None 187*4882a593Smuzhiyun 188*4882a593Smuzhiyun @staticmethod 189*4882a593Smuzhiyun def readPackagesManifest(manifest): 190*4882a593Smuzhiyun if not manifest or not os.path.exists(manifest): 191*4882a593Smuzhiyun raise OSError("Manifest file not exists: %s" % manifest) 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun image_packages = set() 194*4882a593Smuzhiyun with open(manifest, 'r') as f: 195*4882a593Smuzhiyun for line in f.readlines(): 196*4882a593Smuzhiyun line = line.strip() 197*4882a593Smuzhiyun if line and not line.startswith("#"): 198*4882a593Smuzhiyun image_packages.add(line.split()[0]) 199*4882a593Smuzhiyun 200*4882a593Smuzhiyun return image_packages 201*4882a593Smuzhiyun 202*4882a593Smuzhiyun @staticmethod 203*4882a593Smuzhiyun def getHostDumper(cmds, directory): 204*4882a593Smuzhiyun return HostDumper(cmds, directory) 205*4882a593Smuzhiyun 206*4882a593Smuzhiyun def _process_args(self, logger, args): 207*4882a593Smuzhiyun if not args.packages_manifest: 208*4882a593Smuzhiyun raise TypeError('Manifest file not provided') 209*4882a593Smuzhiyun 210*4882a593Smuzhiyun super(OERuntimeTestContextExecutor, self)._process_args(logger, args) 211*4882a593Smuzhiyun 212*4882a593Smuzhiyun target_kwargs = {} 213*4882a593Smuzhiyun target_kwargs['qemuboot'] = args.qemu_boot 214*4882a593Smuzhiyun 215*4882a593Smuzhiyun self.tc_kwargs['init']['target'] = \ 216*4882a593Smuzhiyun OERuntimeTestContextExecutor.getTarget(args.target_type, 217*4882a593Smuzhiyun None, args.target_ip, args.server_ip, **target_kwargs) 218*4882a593Smuzhiyun self.tc_kwargs['init']['host_dumper'] = \ 219*4882a593Smuzhiyun OERuntimeTestContextExecutor.getHostDumper(None, 220*4882a593Smuzhiyun args.host_dumper_dir) 221*4882a593Smuzhiyun self.tc_kwargs['init']['image_packages'] = \ 222*4882a593Smuzhiyun OERuntimeTestContextExecutor.readPackagesManifest( 223*4882a593Smuzhiyun args.packages_manifest) 224*4882a593Smuzhiyun self.tc_kwargs['init']['extract_dir'] = args.extract_dir 225*4882a593Smuzhiyun 226*4882a593Smuzhiyun_executor_class = OERuntimeTestContextExecutor 227