blob: 9c5b0158d5b0f2181d3cf801ac84f665513012f6 [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001#!/usr/bin/env python
2
3# Copyright (c) 2014 Intel Corporation
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License version 2 as
7# published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program; if not, write to the Free Software Foundation, Inc.,
16# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18# DESCRIPTION
19# This script is used to test public autobuilder images on remote hardware.
20# The script is called from a machine that is able download the images from the remote images repository and to connect to the test hardware.
21#
22# test-remote-image --image-type core-image-sato --repo-link http://192.168.10.2/images --required-packages rpm psplash
23#
24# Translation: Build the 'rpm' and 'pslash' packages and test a remote core-image-sato image using the http://192.168.10.2/images repository.
25#
26# You can also use the '-h' option to see some help information.
27
28import os
29import sys
30import argparse
31import logging
32import shutil
33from abc import ABCMeta, abstractmethod
34
35# Add path to scripts/lib in sys.path;
36scripts_path = os.path.abspath(os.path.dirname(os.path.abspath(sys.argv[0])))
37lib_path = scripts_path + '/lib'
38sys.path = sys.path + [lib_path]
39
40import scriptpath
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050041import argparse_oe
Patrick Williamsc124f4f2015-09-15 14:41:29 -050042
43# Add meta/lib to sys.path
44scriptpath.add_oe_lib_path()
45
46import oeqa.utils.ftools as ftools
47from oeqa.utils.commands import runCmd, bitbake, get_bb_var
48
49# Add all lib paths relative to BBPATH to sys.path; this is used to find and import the target controllers.
50for path in get_bb_var('BBPATH').split(":"):
51 sys.path.insert(0, os.path.abspath(os.path.join(path, 'lib')))
52
53# In order to import modules that contain target controllers, we need the bitbake libraries in sys.path .
54bitbakepath = scriptpath.add_bitbake_lib_path()
55if not bitbakepath:
56 sys.stderr.write("Unable to find bitbake by searching parent directory of this script or PATH\n")
57 sys.exit(1)
58
59# create a logger
60def logger_create():
61 log = logging.getLogger('hwauto')
62 log.setLevel(logging.DEBUG)
63
64 fh = logging.FileHandler(filename='hwauto.log', mode='w')
65 fh.setLevel(logging.DEBUG)
66
67 ch = logging.StreamHandler(sys.stdout)
68 ch.setLevel(logging.INFO)
69
70 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
71 fh.setFormatter(formatter)
72 ch.setFormatter(formatter)
73
74 log.addHandler(fh)
75 log.addHandler(ch)
76
77 return log
78
79# instantiate the logger
80log = logger_create()
81
82
83# Define and return the arguments parser for the script
84def get_args_parser():
85 description = "This script is used to run automated runtime tests using remotely published image files. You should prepare the build environment just like building local images and running the tests."
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050086 parser = argparse_oe.ArgumentParser(description=description)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050087 parser.add_argument('--image-types', required=True, action="store", nargs='*', dest="image_types", default=None, help='The image types to test(ex: core-image-minimal).')
88 parser.add_argument('--repo-link', required=True, action="store", type=str, dest="repo_link", default=None, help='The link to the remote images repository.')
89 parser.add_argument('--required-packages', required=False, action="store", nargs='*', dest="required_packages", default=None, help='Required packages for the tests. They will be built before the testing begins.')
90 parser.add_argument('--targetprofile', required=False, action="store", nargs=1, dest="targetprofile", default='AutoTargetProfile', help='The target profile to be used.')
91 parser.add_argument('--repoprofile', required=False, action="store", nargs=1, dest="repoprofile", default='PublicAB', help='The repo profile to be used.')
92 parser.add_argument('--skip-download', required=False, action="store_true", dest="skip_download", default=False, help='Skip downloading the images completely. This needs the correct files to be present in the directory specified by the target profile.')
93 return parser
94
95class BaseTargetProfile(object):
96 """
97 This class defines the meta profile for a specific target (MACHINE type + image type).
98 """
99
100 __metaclass__ = ABCMeta
101
102 def __init__(self, image_type):
103 self.image_type = image_type
104
105 self.kernel_file = None
106 self.rootfs_file = None
107 self.manifest_file = None
108 self.extra_download_files = [] # Extra files (full name) to be downloaded. They should be situated in repo_link
109
110 # This method is used as the standard interface with the target profile classes.
111 # It returns a dictionary containing a list of files and their meaning/description.
112 def get_files_dict(self):
113 files_dict = {}
114
115 if self.kernel_file:
116 files_dict['kernel_file'] = self.kernel_file
117 else:
118 log.error('The target profile did not set a kernel file.')
119 sys.exit(1)
120
121 if self.rootfs_file:
122 files_dict['rootfs_file'] = self.rootfs_file
123 else:
124 log.error('The target profile did not set a rootfs file.')
125 sys.exit(1)
126
127 if self.manifest_file:
128 files_dict['manifest_file'] = self.manifest_file
129 else:
130 log.error('The target profile did not set a manifest file.')
131 sys.exit(1)
132
133 for idx, f in enumerate(self.extra_download_files):
134 files_dict['extra_download_file' + str(idx)] = f
135
136 return files_dict
137
138class AutoTargetProfile(BaseTargetProfile):
139
140 def __init__(self, image_type):
141 super(AutoTargetProfile, self).__init__(image_type)
142 self.image_name = get_bb_var('IMAGE_LINK_NAME', target=image_type)
143 self.kernel_type = get_bb_var('KERNEL_IMAGETYPE', target=image_type)
144 self.controller = self.get_controller()
145
146 self.set_kernel_file()
147 self.set_rootfs_file()
148 self.set_manifest_file()
149 self.set_extra_download_files()
150
151 # Get the controller object that will be used by bitbake.
152 def get_controller(self):
153 from oeqa.controllers.testtargetloader import TestTargetLoader
154
155 target_controller = get_bb_var('TEST_TARGET')
156 bbpath = get_bb_var('BBPATH').split(':')
157
158 if target_controller == "qemu":
159 from oeqa.targetcontrol import QemuTarget
160 controller = QemuTarget
161 else:
162 testtargetloader = TestTargetLoader()
163 controller = testtargetloader.get_controller_module(target_controller, bbpath)
164 return controller
165
166 def set_kernel_file(self):
167 postconfig = "QA_GET_MACHINE = \"${MACHINE}\""
168 machine = get_bb_var('QA_GET_MACHINE', postconfig=postconfig)
169 self.kernel_file = self.kernel_type + '-' + machine + '.bin'
170
171 def set_rootfs_file(self):
172 image_fstypes = get_bb_var('IMAGE_FSTYPES').split(' ')
173 # Get a matching value between target's IMAGE_FSTYPES and the image fstypes suppoerted by the target controller.
174 fstype = self.controller.match_image_fstype(d=None, image_fstypes=image_fstypes)
175 if fstype:
176 self.rootfs_file = self.image_name + '.' + fstype
177 else:
178 log.error("Could not get a compatible image fstype. Check that IMAGE_FSTYPES and the target controller's supported_image_fstypes fileds have common values.")
179 sys.exit(1)
180
181 def set_manifest_file(self):
182 self.manifest_file = self.image_name + ".manifest"
183
184 def set_extra_download_files(self):
185 self.extra_download_files = self.get_controller_extra_files()
186 if not self.extra_download_files:
187 self.extra_download_files = []
188
189 def get_controller_extra_files(self):
190 controller = self.get_controller()
191 return controller.get_extra_files()
192
193
194class BaseRepoProfile(object):
195 """
196 This class defines the meta profile for an images repository.
197 """
198
199 __metaclass__ = ABCMeta
200
201 def __init__(self, repolink, localdir):
202 self.localdir = localdir
203 self.repolink = repolink
204
205 # The following abstract methods are the interfaces to the repository profile classes derived from this abstract class.
206
207 # This method should check the file named 'file_name' if it is different than the upstream one.
208 # Should return False if the image is the same as the upstream and True if it differs.
209 @abstractmethod
210 def check_old_file(self, file_name):
211 pass
212
213 # This method should fetch file_name and create a symlink to localname if set.
214 @abstractmethod
215 def fetch(self, file_name, localname=None):
216 pass
217
218class PublicAB(BaseRepoProfile):
219
220 def __init__(self, repolink, localdir=None):
221 super(PublicAB, self).__init__(repolink, localdir)
222 if localdir is None:
223 self.localdir = os.path.join(os.environ['BUILDDIR'], 'PublicABMirror')
224
225 # Not yet implemented. Always returning True.
226 def check_old_file(self, file_name):
227 return True
228
229 def get_repo_path(self):
230 path = '/machines/'
231
232 postconfig = "QA_GET_MACHINE = \"${MACHINE}\""
233 machine = get_bb_var('QA_GET_MACHINE', postconfig=postconfig)
234 if 'qemu' in machine:
235 path += 'qemu/'
236
237 postconfig = "QA_GET_DISTRO = \"${DISTRO}\""
238 distro = get_bb_var('QA_GET_DISTRO', postconfig=postconfig)
239 path += distro.replace('poky', machine) + '/'
240 return path
241
242
243 def fetch(self, file_name, localname=None):
244 repo_path = self.get_repo_path()
245 link = self.repolink + repo_path + file_name
246
247 self.wget(link, self.localdir, localname)
248
249 def wget(self, link, localdir, localname=None, extraargs=None):
250 wget_cmd = '/usr/bin/env wget -t 2 -T 30 -nv --passive-ftp --no-check-certificate '
251
252 if localname:
253 wget_cmd += ' -O ' + localname + ' '
254
255 if extraargs:
256 wget_cmd += ' ' + extraargs + ' '
257
258 wget_cmd += " -P %s '%s'" % (localdir, link)
259 runCmd(wget_cmd)
260
261class HwAuto():
262
263 def __init__(self, image_types, repolink, required_packages, targetprofile, repoprofile, skip_download):
264 log.info('Initializing..')
265 self.image_types = image_types
266 self.repolink = repolink
267 self.required_packages = required_packages
268 self.targetprofile = targetprofile
269 self.repoprofile = repoprofile
270 self.skip_download = skip_download
271 self.repo = self.get_repo_profile(self.repolink)
272
273 # Get the repository profile; for now we only look inside this module.
274 def get_repo_profile(self, *args, **kwargs):
275 repo = getattr(sys.modules[__name__], self.repoprofile)(*args, **kwargs)
276 log.info("Using repo profile: %s" % repo.__class__.__name__)
277 return repo
278
279 # Get the target profile; for now we only look inside this module.
280 def get_target_profile(self, *args, **kwargs):
281 target = getattr(sys.modules[__name__], self.targetprofile)(*args, **kwargs)
282 log.info("Using target profile: %s" % target.__class__.__name__)
283 return target
284
285 # Run the testimage task on a build while redirecting DEPLOY_DIR_IMAGE to repo.localdir, where the images are downloaded.
286 def runTestimageBuild(self, image_type):
287 log.info("Running the runtime tests for %s.." % image_type)
288 postconfig = "DEPLOY_DIR_IMAGE = \"%s\"" % self.repo.localdir
289 result = bitbake("%s -c testimage" % image_type, ignore_status=True, postconfig=postconfig)
290 testimage_results = ftools.read_file(os.path.join(get_bb_var("T", image_type), "log.do_testimage"))
291 log.info('Runtime tests results for %s:' % image_type)
292 print testimage_results
293 return result
294
295 # Start the procedure!
296 def run(self):
297 if self.required_packages:
298 # Build the required packages for the tests
299 log.info("Building the required packages: %s ." % ', '.join(map(str, self.required_packages)))
300 result = bitbake(self.required_packages, ignore_status=True)
301 if result.status != 0:
302 log.error("Could not build required packages: %s. Output: %s" % (self.required_packages, result.output))
303 sys.exit(1)
304
305 # Build the package repository meta data.
306 log.info("Building the package index.")
307 result = bitbake("package-index", ignore_status=True)
308 if result.status != 0:
309 log.error("Could not build 'package-index'. Output: %s" % result.output)
310 sys.exit(1)
311
312 # Create the directory structure for the images to be downloaded
313 log.info("Creating directory structure %s" % self.repo.localdir)
314 if not os.path.exists(self.repo.localdir):
315 os.makedirs(self.repo.localdir)
316
317 # For each image type, download the needed files and run the tests.
318 noissuesfound = True
319 for image_type in self.image_types:
320 if self.skip_download:
321 log.info("Skipping downloading the images..")
322 else:
323 target = self.get_target_profile(image_type)
324 files_dict = target.get_files_dict()
325 log.info("Downloading files for %s" % image_type)
326 for f in files_dict:
327 if self.repo.check_old_file(files_dict[f]):
328 filepath = os.path.join(self.repo.localdir, files_dict[f])
329 if os.path.exists(filepath):
330 os.remove(filepath)
331 self.repo.fetch(files_dict[f])
332
333 result = self.runTestimageBuild(image_type)
334 if result.status != 0:
335 noissuesfound = False
336
337 if noissuesfound:
338 log.info('Finished. No issues found.')
339 else:
340 log.error('Finished. Some runtime tests have failed. Returning non-0 status code.')
341 sys.exit(1)
342
343
344
345def main():
346
347 parser = get_args_parser()
348 args = parser.parse_args()
349
350 hwauto = HwAuto(image_types=args.image_types, repolink=args.repo_link, required_packages=args.required_packages, targetprofile=args.targetprofile, repoprofile=args.repoprofile, skip_download=args.skip_download)
351
352 hwauto.run()
353
354if __name__ == "__main__":
355 try:
356 ret = main()
357 except Exception:
358 ret = 1
359 import traceback
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500360 traceback.print_exc()
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500361 sys.exit(ret)