xref: /OK3568_Linux_fs/yocto/poky/meta/lib/oeqa/runtime/context.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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