summaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
authorCorneliu Stoicescu <corneliux.stoicescu@intel.com>2014-06-06 22:14:38 +0300
committerRichard Purdie <richard.purdie@linuxfoundation.org>2014-06-13 12:52:21 +0100
commit7c7a25c28a3c196301166ec5fb886d226cea19f1 (patch)
treef4f77d1e6db666062a069af70fcb09bda90506a6 /scripts
parent5456f0e58ebf07a9a639381caddf2d616623c190 (diff)
downloadpoky-7c7a25c28a3c196301166ec5fb886d226cea19f1.tar.gz
scripts/test-remote-image: Add script for running runtime tests on remotely built images
YB: #6254 Adding a new script that will fetch image files from a remote images repository. These images will then be used for local runtime testing. Use the '-h' option for more details on usage. (From OE-Core rev: 5ebe9c57efb9715d58691d7fa75ccf06fb5d4b18) Signed-off-by: Corneliu Stoicescu <corneliux.stoicescu@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/test-remote-image340
1 files changed, 340 insertions, 0 deletions
diff --git a/scripts/test-remote-image b/scripts/test-remote-image
new file mode 100755
index 0000000000..6da66724b9
--- /dev/null
+++ b/scripts/test-remote-image
@@ -0,0 +1,340 @@
1#!/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 meta/lib to sys.path
36sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'meta/lib')))
37
38import oeqa.utils.ftools as ftools
39from oeqa.utils.commands import runCmd, bitbake, get_bb_var
40
41# Add all lib paths relative to BBPATH to sys.path; this is used to find and import the target controllers.
42for path in get_bb_var('BBPATH').split(":"):
43 sys.path.insert(0, os.path.abspath(os.path.join(path, 'lib')))
44
45# In order to import modules that contain target controllers, we need the bitbake libraries in sys.path .
46sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'bitbake/lib')))
47
48# create a logger
49def logger_create():
50 log = logging.getLogger('hwauto')
51 log.setLevel(logging.DEBUG)
52
53 fh = logging.FileHandler(filename='hwauto.log', mode='w')
54 fh.setLevel(logging.DEBUG)
55
56 ch = logging.StreamHandler(sys.stdout)
57 ch.setLevel(logging.INFO)
58
59 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
60 fh.setFormatter(formatter)
61 ch.setFormatter(formatter)
62
63 log.addHandler(fh)
64 log.addHandler(ch)
65
66 return log
67
68# instantiate the logger
69log = logger_create()
70
71
72# Define and return the arguments parser for the script
73def get_args_parser():
74 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."
75 parser = argparse.ArgumentParser(description=description)
76 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).')
77 parser.add_argument('--repo-link', required=True, action="store", type=str, dest="repo_link", default=None, help='The link to the remote images repository.')
78 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.')
79 parser.add_argument('--targetprofile', required=False, action="store", nargs=1, dest="targetprofile", default='AutoTargetProfile', help='The target profile to be used.')
80 parser.add_argument('--repoprofile', required=False, action="store", nargs=1, dest="repoprofile", default='PublicAB', help='The repo profile to be used.')
81 return parser
82
83class BaseTargetProfile(object):
84 """
85 This class defines the meta profile for a specific target (MACHINE type + image type).
86 """
87
88 __metaclass__ = ABCMeta
89
90 def __init__(self, image_type):
91 self.image_type = image_type
92
93 self.kernel_file = None
94 self.rootfs_file = None
95 self.manifest_file = None
96 self.extra_download_files = [] # Extra files (full name) to be downloaded. They should be situated in repo_link
97
98 # This method is used as the standard interface with the target profile classes.
99 # It returns a dictionary containing a list of files and their meaning/description.
100 def get_files_dict(self):
101 files_dict = {}
102
103 if self.kernel_file:
104 files_dict['kernel_file'] = self.kernel_file
105 else:
106 log.error('The target profile did not set a kernel file.')
107 sys.exit(1)
108
109 if self.rootfs_file:
110 files_dict['rootfs_file'] = self.rootfs_file
111 else:
112 log.error('The target profile did not set a rootfs file.')
113 sys.exit(1)
114
115 if self.manifest_file:
116 files_dict['manifest_file'] = self.manifest_file
117 else:
118 log.error('The target profile did not set a manifest file.')
119 sys.exit(1)
120
121 for idx, f in enumerate(self.extra_download_files):
122 files_dict['extra_download_file' + str(idx)] = f
123
124 return files_dict
125
126class AutoTargetProfile(BaseTargetProfile):
127
128 def __init__(self, image_type):
129 super(AutoTargetProfile, self).__init__(image_type)
130 self.image_name = get_bb_var('IMAGE_LINK_NAME', target=image_type)
131 self.kernel_type = get_bb_var('KERNEL_IMAGETYPE', target=image_type)
132 self.controller = self.get_controller()
133
134 self.set_kernel_file()
135 self.set_rootfs_file()
136 self.set_manifest_file()
137 self.set_extra_download_files()
138
139
140 def get_controller(self):
141 from oeqa.controllers.testtargetloader import TestTargetLoader
142
143 target_controller = get_bb_var('TEST_TARGET')
144 bbpath = get_bb_var('BBPATH').split(':')
145
146 if target_controller == "qemu":
147 from oeqa.targetcontrol import QemuTarget
148 controller = QemuTarget
149 else:
150 testtargetloader = TestTargetLoader()
151 controller = testtargetloader.get_controller_module(target_controller, bbpath)
152 return controller
153
154 def set_kernel_file(self):
155 postconfig = "QA_GET_MACHINE = \"${MACHINE}\""
156 machine = get_bb_var('QA_GET_MACHINE', postconfig=postconfig)
157 self.kernel_file = self.kernel_type + '-' + machine + '.bin'
158
159 def set_rootfs_file(self):
160 image_fstypes = get_bb_var('IMAGE_FSTYPES').split(' ')
161 fstype = self.controller.get_image_fstype(d=None, image_fstypes=image_fstypes)
162 self.rootfs_file = self.image_name + '.' + fstype
163
164 def set_manifest_file(self):
165 self.manifest_file = self.image_name + ".manifest"
166
167 def set_extra_download_files(self):
168 self.extra_download_files = self.get_controller_extra_files()
169 if not self.extra_download_files:
170 self.extra_download_files = []
171
172 def get_controller_extra_files(self):
173 controller = self.get_controller()
174 return controller.get_extra_files()
175
176
177class BaseRepoProfile(object):
178 """
179 This class defines the meta profile for an images repository.
180 """
181
182 __metaclass__ = ABCMeta
183
184 def __init__(self, repolink, localdir):
185 self.localdir = localdir
186 self.repolink = repolink
187
188 # The following abstract methods are the interfaces to the repository profile classes derived from this abstract class.
189
190 # This method should check the file named 'file_name' if it is different than the upstream one.
191 # Should return False if the image is the same as the upstream and True if it differs.
192 @abstractmethod
193 def check_old_file(self, file_name):
194 pass
195
196 # This method should fetch file_name and create a symlink to localname if set.
197 @abstractmethod
198 def fetch(self, file_name, localname=None):
199 pass
200
201class PublicAB(BaseRepoProfile):
202
203 def __init__(self, repolink, localdir=None):
204 super(PublicAB, self).__init__(repolink, localdir)
205 if localdir is None:
206 self.localdir = os.path.join(os.environ['BUILDDIR'], 'PublicABMirror')
207
208 # Not yet implemented. Always returning True.
209 def check_old_file(self, file_name):
210 return True
211
212 def get_repo_path(self):
213 path = '/machines/'
214
215 postconfig = "QA_GET_MACHINE = \"${MACHINE}\""
216 machine = get_bb_var('QA_GET_MACHINE', postconfig=postconfig)
217 if 'qemu' in machine:
218 path += 'qemu/'
219
220 postconfig = "QA_GET_DISTRO = \"${DISTRO}\""
221 distro = get_bb_var('QA_GET_DISTRO', postconfig=postconfig)
222 path += distro.replace('poky', machine) + '/'
223 return path
224
225
226 def fetch(self, file_name, localname=None):
227 repo_path = self.get_repo_path()
228 link = self.repolink + repo_path + file_name
229
230 self.wget(link, self.localdir, localname)
231
232 def wget(self, link, localdir, localname=None, extraargs=None):
233 wget_cmd = '/usr/bin/env wget -t 2 -T 30 -nv --passive-ftp --no-check-certificate '
234
235 if localname:
236 wget_cmd += ' -O ' + localname + ' '
237
238 if extraargs:
239 wget_cmd += ' ' + extraargs + ' '
240
241 wget_cmd += " -P %s '%s'" % (localdir, link)
242 runCmd(wget_cmd)
243
244class HwAuto():
245
246 def __init__(self, image_types, repolink, required_packages, targetprofile, repoprofile):
247 log.info('Initializing..')
248 self.image_types = image_types
249 self.repolink = repolink
250 self.required_packages = required_packages
251 self.targetprofile = targetprofile
252 self.repoprofile = repoprofile
253 self.repo = self.get_repo_profile(self.repolink)
254
255 # Get the repository profile; for now we only look inside this module.
256 def get_repo_profile(self, *args, **kwargs):
257 repo = getattr(sys.modules[__name__], self.repoprofile)(*args, **kwargs)
258 log.info("Using repo profile: %s" % repo.__class__.__name__)
259 return repo
260
261 # Get the target profile; for now we only look inside this module.
262 def get_target_profile(self, *args, **kwargs):
263 target = getattr(sys.modules[__name__], self.targetprofile)(*args, **kwargs)
264 log.info("Using target profile: %s" % target.__class__.__name__)
265 return target
266
267 # Run the testimage task on a build while redirecting DEPLOY_DIR_IMAGE to repo.localdir, where the images are downloaded.
268 def runTestimageBuild(self, image_type):
269 log.info("Running the runtime tests for %s.." % image_type)
270 postconfig = "DEPLOY_DIR_IMAGE = \"%s\"" % self.repo.localdir
271 result = bitbake("%s -c testimage" % image_type, ignore_status=True, postconfig=postconfig)
272 testimage_results = ftools.read_file(os.path.join(get_bb_var("T", image_type), "log.do_testimage"))
273 log.info('Runtime tests results for %s:' % image_type)
274 print testimage_results
275 return result
276
277 # Start the procedure!
278 def run(self):
279 if self.required_packages:
280 # Build the required packages for the tests
281 log.info("Building the required packages: %s ." % ', '.join(map(str, self.required_packages)))
282 result = bitbake(self.required_packages, ignore_status=True)
283 if result.status != 0:
284 log.error("Could not build required packages: %s. Output: %s" % (self.required_packages, result.output))
285 sys.exit(1)
286
287 # Build the package repository meta data.
288 log.info("Building the package index.")
289 result = bitbake("package-index", ignore_status=True)
290 if result.status != 0:
291 log.error("Could not build 'package-index'. Output: %s" % result.output)
292 sys.exit(1)
293
294 # Create the directory structure for the images to be downloaded
295 log.info("Creating directory structure %s" % self.repo.localdir)
296 if not os.path.exists(self.repo.localdir):
297 os.makedirs(self.repo.localdir)
298
299 # For each image type, download the needed files and run the tests.
300 noissuesfound = True
301 for image_type in self.image_types:
302 target = self.get_target_profile(image_type)
303 files_dict = target.get_files_dict()
304 log.info("Downloading files for %s" % image_type)
305 for f in files_dict:
306 if self.repo.check_old_file(files_dict[f]):
307 filepath = os.path.join(self.repo.localdir, files_dict[f])
308 if os.path.exists(filepath):
309 os.remove(filepath)
310 self.repo.fetch(files_dict[f])
311
312 result = self.runTestimageBuild(image_type)
313 if result.status != 0:
314 noissuesfound = False
315
316 if noissuesfound:
317 log.info('Finished. No issues found.')
318 else:
319 log.error('Finished. Some runtime tests have failed. Returning non-0 status code.')
320 sys.exit(1)
321
322
323
324def main():
325
326 parser = get_args_parser()
327 args = parser.parse_args()
328
329 hwauto = HwAuto(image_types=args.image_types, repolink=args.repo_link, required_packages=args.required_packages, targetprofile=args.targetprofile, repoprofile=args.repoprofile)
330
331 hwauto.run()
332
333if __name__ == "__main__":
334 try:
335 ret = main()
336 except Exception:
337 ret = 1
338 import traceback
339 traceback.print_exc(5)
340 sys.exit(ret)