Merge lp:~javier.collado/utah/bug1089884 into lp:utah

Proposed by Javier Collado
Status: Merged
Merged at revision: 792
Proposed branch: lp:~javier.collado/utah/bug1089884
Merge into: lp:utah
Diff against target: 271 lines (+133/-19)
2 files modified
utah/isotest/iso_static_validation.py (+127/-14)
utah/run.py (+6/-5)
To merge this branch: bzr merge lp:~javier.collado/utah/bug1089884
Reviewer Review Type Date Requested Status
Joe Talbott (community) Approve
Javier Collado (community) Needs Resubmitting
Review via email: mp+139698@code.launchpad.net

Description of the change

This branch updates the ISO validation script to generate a yaml file report
that can be parsed by the QA dashboard.

Aside from this, the script output has been modified to:
- Include all debug logs
- Print backtraces and skipped test cases

Backtraces were already in the script, but was lost when switiching to running
the test suite manually to process results to generate the yaml report.

To post a comment you must log in.
Revision history for this message
Javier Collado (javier.collado) wrote :

The changes in this branch should be enough to make the ISO validation script
friendlier. However, I still need to look at the setup-jobs.py script to
update the job so that the yaml file is archived as an artifact in jenkins.

Please let me know if some other thing is missing. Thanks.

Revision history for this message
Max Brustkern (nuclearbob) wrote :

There are a few things I'd like to see changed here, but I'm not sure they're necessary to deployment.

First, I ran this on a raring ISO from last week, and it passed. I know we've discussed changing the tests to support validating old ISOs, but the current default behavior is to fail on an old ISO, which I think is appropriate for daily smoke testing, where we always want to be testing the latest ISO.

Second, and less important, I don't like that the default behavior of this script is to write a file to my current working directory. In my view, the only time that's useful is when we're running under jenkins, and if we are running under jenkins, there are several ways we can handle that. We can write the yaml to standard out by default (and redirect it to a file in the jenkins job.) We can default to writing a file to a logs directory, and either add a parameter to write that file to a specific location (such as the current working directory) or provide clear and consistent output regarding where that file is written so that the output can be parsed and the file picked up. If I'm testing the scripts, or manually validating an ISO for some reason, I'm going to end up with extra files in my branch that need to be cleaned up.

lp:~javier.collado/utah/bug1089884 updated
800. By Javier Collado

Added log directory default (relative path not used anymore)

801. By Javier Collado

Fixed error/failure counters

Bad subclassing of `unittest.TestResult` was the problem. Using `super` to call
the default implementation, brings the counters correct value back.

Revision history for this message
Javier Collado (javier.collado) wrote :

Fixed both issues in previous comment.

review: Needs Resubmitting
lp:~javier.collado/utah/bug1089884 updated
802. By Javier Collado

Fixed log filename printed to stdout

803. By Javier Collado

Fixed unknown `uname` should be an empty array

Revision history for this message
Javier Collado (javier.collado) wrote :

Log file name is now printed to stdout (and prefixed with a tab) and `uname` is
set to an empty array.

review: Needs Resubmitting
lp:~javier.collado/utah/bug1089884 updated
804. By Javier Collado

Fixed timestamp format

Revision history for this message
Javier Collado (javier.collado) wrote :

Generated report example:

---
arch: i386
build_number: '20121108.2'
commands: []
errors: 5
failures: 0
fetch_errors: 0
install_type: desktop
media-info: Ubuntu 13.04 "Raring Ringtail" - Alpha i386 (20121108.2)
passes: 5
ran_at: 2012-12-13_15-06-22
release: raring
runlist: iso_static_validation
uname: []

lp:~javier.collado/utah/bug1089884 updated
805. By Javier Collado

Fixed use different timestamp format in the report and in the filename

806. By Javier Collado

Added name field to the report

807. By Javier Collado

Expanded comment about `name` and `runlist` fields

Revision history for this message
Javier Collado (javier.collado) wrote :

New format after the fixes above:

---
arch: i386
build_number: '20121108.2'
commands: []
errors: 5
failures: 0
fetch_errors: 0
install_type: desktop
media-info: Ubuntu 13.04 "Raring Ringtail" - Alpha i386 (20121108.2)
name: iso_static_validation
passes: 5
ran_at: '2012-12-13 16:18:35'
release: raring
runlist: iso_static_validation
uname: []

review: Needs Resubmitting
lp:~javier.collado/utah/bug1089884 updated
808. By Javier Collado

Moved logging message outside of loop to get it just once

Revision history for this message
Joe Talbott (joetalbott) wrote :

Looks good to me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'utah/isotest/iso_static_validation.py'
2--- utah/isotest/iso_static_validation.py 2012-12-07 18:04:00 +0000
3+++ utah/isotest/iso_static_validation.py 2012-12-13 17:31:21 +0000
4@@ -20,7 +20,7 @@
5 #
6 # This file is part of utah.
7 #
8-# iso_satatic_validation is free software: you can redistribute it
9+# iso_static_validation is free software: you can redistribute it
10 # and/or modify it under the terms of the GNU General Public License
11 # as published by the Free Software Foundation, either version 3 of
12 # the License, or (at your option) any later version.
13@@ -37,6 +37,13 @@
14
15 # TODO: support for non-daily images
16
17+"""ISO static validation.
18+
19+The test cases in this scripts are used to make sure that an ISO image isn't
20+broken and can be used for smoke testing.
21+
22+"""
23+
24 import os
25 import argparse
26 import sys
27@@ -48,6 +55,11 @@
28 import re
29 import tempfile
30 import shutil
31+import time
32+import platform
33+from traceback import format_exception
34+
35+import yaml
36
37 lib_path = os.path.abspath('../')
38 sys.path.append(lib_path)
39@@ -57,6 +69,7 @@
40 # Defaults
41 DEFAULT_ISOROOT = os.path.expanduser('~/Downloads')
42 DEFAULT_URL = 'http://cdimage.ubuntu.com/'
43+DEFAULT_LOG_DIR = '/var/log/utah/'
44
45 #constants
46 ONE_MB_BLOCK = 2 ** 20
47@@ -66,7 +79,26 @@
48 parser.add_argument('--name')
49 args = parser.parse_args()
50
51-logging.basicConfig(level=logging.INFO)
52+
53+def configure_logging(log_level=logging.DEBUG):
54+ """Configure logging to stderr using the passed log_level.
55+
56+ :param log_level: Log level to be used
57+ :type log_level: int
58+
59+ """
60+ logger = logging.getLogger()
61+ logger.setLevel(logging.DEBUG)
62+ formatter = logging.Formatter('%(levelname)s: %(message)s')
63+
64+ log_handler = logging.StreamHandler()
65+ log_handler.setFormatter(formatter)
66+ log_handler.setLevel(log_level)
67+
68+ logger.addHandler(log_handler)
69+
70+
71+configure_logging()
72
73 if args.name is None or '.iso' not in args.name:
74 print 'The usage:', sys.argv[0], '--name release-variant-arch.iso'
75@@ -101,10 +133,12 @@
76 try:
77 cls.temp_dir = tempfile.mkdtemp()
78 except OSError:
79- logging.error("Creating temp dirrectory failed")
80+ logging.error("Creating temp directory failed")
81 unittest.fail("Setup error")
82
83 def setUp(self):
84+ logging.info('\n{separator}\n{id}\n{separator}'
85+ .format(separator='-' * 80, id=self.id()))
86 self.block_size = ONE_MB_BLOCK
87 self.iso_location = iso_location
88 logging.debug('Using iso at: ' + self.iso_location)
89@@ -198,8 +232,8 @@
90 logging.debug('Checking for error in extracting the file list')
91 self.assertEqual(stderr, '')
92
93+ logging.debug('Checking that each list entry is in the iso')
94 for list_entry in list_repository:
95- logging.debug('Checking that each list entry is in the iso')
96 self.assertIn(list_entry[1:].rstrip(), stdout)
97
98 # Test if the manfest is the same as that is given in the server
99@@ -221,8 +255,8 @@
100 manifest_path = self.iso.extract('casper/filesystem.manifest',
101 self.temp_dir)
102 manifest_local = open(manifest_path)
103+ logging.debug('Checking manifest are the same in iso and repo')
104 for manifest_entry in manifest:
105- logging.debug('Checking manifest are the same in iso and repo')
106 self.assertIn(manifest_entry, manifest_local)
107
108 # Test if the buildid present in the download server is
109@@ -278,8 +312,8 @@
110 # wubi is not shipped in raring
111 exclude_files.append('autorun.inf')
112 exclude_files.append('wubi.exe')
113+ logging.debug('Check if relevant files are present in the iso')
114 for list_server in files_list:
115- logging.debug('Check if relevant files are present in the iso')
116 path = list_server.rstrip()
117 if path in exclude_files:
118 logging.debug(path + ' excluded based on release')
119@@ -359,12 +393,17 @@
120 self.assertIn("Squashfs filesystem", stdout)
121
122 #unsquashfs extracts only in pwd
123- os.chdir(self.temp_dir)
124-
125- cmd = ["unsquashfs", "-f", squashfs_path, "-l", "vmlinuz"]
126- output = subprocess.Popen(cmd, stdout=subprocess.PIPE,
127- stderr=subprocess.PIPE)
128- (stdout, stderr) = output.communicate()
129+ cwd = os.getcwd()
130+ try:
131+ os.chdir(self.temp_dir)
132+
133+ cmd = ["unsquashfs", "-f", squashfs_path, "-l", "vmlinuz"]
134+ output = subprocess.Popen(cmd, stdout=subprocess.PIPE,
135+ stderr=subprocess.PIPE)
136+ (stdout, stderr) = output.communicate()
137+ finally:
138+ os.chdir(cwd)
139+
140 logging.debug('Check that vmlinuz could be extracted')
141 self.assertEqual(stderr, '')
142 self.assertIn("created 1 directories", stdout)
143@@ -378,6 +417,80 @@
144 logging.error("Removing the temp directory failed")
145 unittest.fail("Cleanup error")
146
147+
148+def get_report_data(runlist, results):
149+ errors = len(results.errors)
150+ failures = len(results.failures)
151+ skipped = len(results.skipped)
152+ passes = results.testsRun - skipped - errors - failures
153+ report_timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime())
154+
155+ # Note: Ideally 'name' is the runlist name and 'runlist' the path to the
156+ # runlist file. Given that there isn't a real runlist file here, the name
157+ # is being used for both fields.
158+ report_data = {
159+ 'name': runlist,
160+ 'runlist': runlist,
161+ 'media-info': iso.media_info,
162+ 'build_number': iso.buildnumber,
163+ 'release': iso.series,
164+ 'ran_at': report_timestamp,
165+ 'install_type': iso.installtype,
166+ 'arch': iso.arch,
167+ 'commands': [],
168+ 'fetch_errors': 0,
169+ 'errors': errors,
170+ 'failures': failures,
171+ 'passes': passes,
172+ 'uname': [],
173+ }
174+
175+ return report_data
176+
177+
178+class ISOResult(unittest.TestResult):
179+ def _print_error(self, test, error):
180+ logging.error(test)
181+ logging.error(''.join(format_exception(*error)))
182+
183+ def addError(self, test, error):
184+ super(ISOResult, self).addError(test, error)
185+ self._print_error(test, error)
186+
187+ def addFailure(self, test, error):
188+ super(ISOResult, self).addError(test, error)
189+ self._print_error(test, error)
190+
191+ def addSkip(self, test, reason):
192+ logging.info('\n{separator}\n{id}\n{separator}'
193+ .format(separator='-' * 80, id=test.id()))
194+ logging.info('Skipped: {}'.format(reason))
195+
196+
197+def main():
198+ """Run test cases and write qa dashboard friendly report."""
199+ suite = unittest.TestLoader().loadTestsFromTestCase(TestValidateISO)
200+ results = ISOResult()
201+ results.buffer = True
202+ suite.run(results)
203+
204+ fake_runlist = 'iso_static_validation'
205+ report_data = get_report_data(fake_runlist, results)
206+ report = yaml.dump(report_data,
207+ explicit_start='---',
208+ default_flow_style=False)
209+ filename_timestamp = time.strftime('%Y-%m-%d_%H-%M-%S', time.gmtime())
210+ log_filename = ('{machine}_{runlist}_{timestamp}.{suffix}'
211+ .format(machine=platform.node(),
212+ runlist=fake_runlist,
213+ timestamp=filename_timestamp,
214+ suffix='yaml'))
215+ absolute_log_filename = os.path.join(DEFAULT_LOG_DIR, log_filename)
216+ with open(absolute_log_filename, 'w') as f:
217+ f.write(report)
218+ print('Test log copied to\n\t{}'.format(absolute_log_filename))
219+
220+ return 0 if results.wasSuccessful() else 1
221+
222 if __name__ == '__main__':
223- from test import test_support
224- test_support.run_unittest(TestValidateISO)
225+ sys.exit(main())
226
227=== modified file 'utah/run.py'
228--- utah/run.py 2012-12-06 16:30:34 +0000
229+++ utah/run.py 2012-12-13 17:31:21 +0000
230@@ -59,16 +59,16 @@
231 report will be generated.
232
233 """
234- timestamp = time.strftime('%Y-%m-%d_%H-%m-%S', time.gmtime())
235-
236+ gmtime = time.gmtime()
237 if report is None:
238 # Generate default report with empty results to fake client output
239+ report_timestamp = time.strftime('%Y-%m-%d %H:%M:%S', gmtime)
240 default_report = {
241 'runlist': runlist,
242 'media-info': machine.image.media_info,
243 'build_number': machine.image.buildnumber,
244 'release': machine.image.series,
245- 'ran_at': timestamp,
246+ 'ran_at': report_timestamp,
247 'install_type': machine.installtype,
248 'arch': machine.arch,
249 'commands': [],
250@@ -76,7 +76,7 @@
251 'errors': 0,
252 'failures': 0,
253 'passes': 0,
254- 'uname': '',
255+ 'uname': [],
256
257
258 }
259@@ -91,10 +91,11 @@
260 .format(report_type))
261
262 listname = os.path.basename(runlist)
263+ filename_timestamp = time.strftime('%Y-%m-%d %H:%M:%S', gmtime)
264 log_filename = ('{machine}_{runlist}_{timestamp}.{suffix}'
265 .format(machine=machine.name,
266 runlist=listname,
267- timestamp=timestamp,
268+ timestamp=filename_timestamp,
269 suffix=report_type))
270 locallog = os.path.join(config.logpath, log_filename)
271 locallog = os.path.normpath(locallog)

Subscribers

People subscribed via source and target branches