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

Proposed by Javier Collado
Status: Merged
Approved by: Joe Talbott
Approved revision: 774
Merged at revision: 766
Proposed branch: lp:~javier.collado/utah/bug1084083
Merge into: lp:utah
Diff against target: 273 lines (+123/-45)
4 files modified
examples/utah_syslog.sh (+1/-1)
utah/iso.py (+11/-4)
utah/parser.py (+16/-0)
utah/run.py (+95/-40)
To merge this branch: bzr merge lp:~javier.collado/utah/bug1084083
Reviewer Review Type Date Requested Status
Joe Talbott (community) Approve
Javier Collado (community) Needs Resubmitting
Gema Gomez (community) Approve
Review via email: mp+137167@code.launchpad.net

Description of the change

This branch writes a default yaml report file for each runlist when a problem
happens and the real yaml file cannot be retrieved.

An example of the contents of the default file is:
---
arch: amd64
build_number: '20121129'
commands: []
errors: 0
failures: 0
fetch_errors: 0
install_type: desktop
media-info: Ubuntu 13.04 "Raring Ringtail" - Alpha amd64 (20121129)
ran_at: 2012-11-30_12-11-55
release: raring
runlist: /tmp/utah_W9b_VV/default.run
uname: ''

The fields that it contains all the fields suggested by Joe and it validates
according to the schema in parser.py (some of the fields that were missing have
been added to the schema). However, there isn't any calculation about the
number of test cases that have been skipped. Please let me know if this is fine
or there's a real need to provide some more data.

To post a comment you must log in.
Revision history for this message
Gema Gomez (gema) wrote :

Looks good to me!

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

'passes' seems to be missing from that output.

review: Needs Information
lp:~javier.collado/utah/bug1084083 updated
774. By Javier Collado

Added passes to the default report

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

Good catch! I added passes to the default report and reviewed the schema (all
fields should be included now).

By the way, I've noticed that 'media-info' is the only field that uses a dash
in its name, all the other ones use an underscore.

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

Looks good to me. Thanks.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/utah_syslog.sh'
2--- examples/utah_syslog.sh 2012-07-31 14:22:31 +0000
3+++ examples/utah_syslog.sh 2012-11-30 14:52:20 +0000
4@@ -2,5 +2,5 @@
5
6 for MACHINE in $@
7 do
8- cat /var/log/utah/$(echo "$MACHINE" | grep -o "utah-[0-9]*")-*.syslog.log
9+ cat /var/log/utah/$(echo "$MACHINE" | grep -o "utah-[0-9]\+")-*.syslog.log
10 done
11
12=== modified file 'utah/iso.py'
13--- utah/iso.py 2012-11-16 19:27:27 +0000
14+++ utah/iso.py 2012-11-30 14:52:20 +0000
15@@ -8,6 +8,7 @@
16 import subprocess
17 import sys
18 import urllib
19+import re
20
21 from hashlib import md5
22
23@@ -62,8 +63,10 @@
24 # Ubuntu 12.10 "Quantal Quetzal" - Alpha amd64 (20120820)
25 # i.e. Ubuntu Version (LTS) "Series Codename" - Alpha/Beta/Release
26 # arch (buildnumber)
27+ self.media_info = self.dump(self.infofile).communicate()[0]
28 self.arch = self.getarch()
29 self.series = self.getseries()
30+ self.buildnumber = self.getbuildnumber()
31
32 def _loggersetup(self):
33 """
34@@ -118,8 +121,7 @@
35 """
36 Unpack the image's info file to get the arch.
37 """
38- stdout = self.dump(self.infofile).communicate()[0]
39- arch = stdout.split()[-2]
40+ arch = self.media_info.split()[-2]
41 self.logger.info('Arch is: ' + arch)
42 return arch
43
44@@ -127,14 +129,19 @@
45 """
46 Unpack the image's info file to get the series.
47 """
48- stdout = self.dump(self.infofile).communicate()[0]
49- for word in stdout.split():
50+ for word in self.media_info.split():
51 if word.startswith('"'):
52 series = word.strip('"').lower()
53 break
54 self.logger.info('Series is ' + series)
55 return series
56
57+ def getbuildnumber(self):
58+ """Unpack the image's info file to get the build number."""
59+ match = re.search('.*\(([0-9.]+)\)$', self.media_info)
60+ build_number = match.group(1) if match else '?'
61+ return build_number
62+
63 def dldisplay(self, blocks, size, total):
64 """
65 Display download information.
66
67=== modified file 'utah/parser.py'
68--- utah/parser.py 2012-11-30 09:33:04 +0000
69+++ utah/parser.py 2012-11-30 14:52:20 +0000
70@@ -40,6 +40,10 @@
71 'type': 'string',
72 'required': True,
73 },
74+ 'ran_at': {
75+ 'type': 'string',
76+ 'required': True,
77+ },
78 'commands': {
79 'type': 'array',
80 'items': {'type': COMMAND_SCHEMA},
81@@ -74,6 +78,18 @@
82 'type': 'string',
83 'required': True,
84 },
85+ 'arch': {
86+ 'type': 'string',
87+ 'required': True,
88+ },
89+ 'release': {
90+ 'type': 'string',
91+ 'required': True,
92+ },
93+ 'build_number': {
94+ 'type': 'string',
95+ 'required': True,
96+ },
97 },
98 }
99
100
101=== modified file 'utah/run.py'
102--- utah/run.py 2012-11-29 16:37:23 +0000
103+++ utah/run.py 2012-11-30 14:52:20 +0000
104@@ -5,6 +5,9 @@
105 import os
106 import time
107 import urllib
108+import json
109+import yaml
110+import traceback
111
112 from utah import config
113 from utah.exceptions import UTAHException
114@@ -14,68 +17,120 @@
115 """
116 Run some tests and retrieve results.
117 """
118- # Since we're running from a script, output INFO
119- for logger in [machine.logger] + machine.logger.handlers:
120- if logger.level > logging.INFO:
121- logger.setLevel(logging.INFO)
122-
123- # Write the machine name to standard out for log gathering
124- print('Running on machine: ' + machine.name)
125-
126- if args.json:
127- extraopts = ' -f json'
128- suffix = 'json'
129- else:
130- extraopts = ''
131- suffix = 'yaml'
132-
133- locallogs = []
134-
135- machine.installclient()
136-
137- extraopts += " --install-type " + machine.installtype
138-
139 def _run(runlist):
140 """Run a single runlist."""
141- locallist = urllib.urlretrieve(runlist)[0]
142- machine.uploadfiles([locallist], os.path.normpath('/tmp'))
143+ stdout = None
144
145- options = ' -r /tmp/' + os.path.basename(locallist)
146- utah_command = 'utah' + extraopts + options
147 try:
148- # Server will return same code as client
149+ locallist = urllib.urlretrieve(runlist)[0]
150+ machine.uploadfiles([locallist], os.path.normpath('/tmp'))
151+
152+ options = ' -r /tmp/' + os.path.basename(locallist)
153+ utah_command = 'utah' + extraopts + options
154 exitstatus, stdout, _stderr = machine.run(utah_command, root=True)
155 except UTAHException as error:
156 machine.logger.error('Failed to run test: ' + str(error))
157- return 1
158+ exitstatus = 1
159+
160+ _write(runlist, stdout)
161+
162+ return exitstatus
163+
164+ def _write(runlist, report=None):
165+ """Write runlist report.
166+
167+ Report will be written using appropriate filename based on the runlist.
168+ If report is None becase there was some problem, then a default empty
169+ report will be generated.
170+
171+ """
172+ timestamp = time.strftime('%Y-%m-%d_%H-%m-%S', time.gmtime())
173+
174+ if report is None:
175+ # Generate default report with empty results to fake client output
176+ default_report = {
177+ 'runlist': runlist,
178+ 'media-info': machine.image.media_info,
179+ 'build_number': machine.image.buildnumber,
180+ 'release': machine.image.series,
181+ 'ran_at': timestamp,
182+ 'install_type': machine.installtype,
183+ 'arch': machine.arch,
184+ 'commands': [],
185+ 'fetch_errors': 0,
186+ 'errors': 0,
187+ 'failures': 0,
188+ 'passes': 0,
189+ 'uname': '',
190+
191+
192+ }
193+ if report_type == 'json':
194+ report = json.dumps(default_report)
195+ elif report_type == 'yaml':
196+ report = yaml.dump(default_report,
197+ explicit_start='---',
198+ default_flow_style=False)
199+ else:
200+ raise ValueError('Unexpected report type: {}'
201+ .format(report_type))
202
203 listname = os.path.basename(runlist)
204- timestamp = time.strftime('%Y-%m-%d_%H-%m-%S', time.gmtime())
205 log_filename = ('{machine}_{runlist}_{timestamp}.{suffix}'
206 .format(machine=machine.name,
207 runlist=listname,
208 timestamp=timestamp,
209- suffix=suffix))
210+ suffix=report_type))
211 locallog = os.path.join(config.logpath, log_filename)
212 locallog = os.path.normpath(locallog)
213 with open(locallog, 'w') as f:
214- f.write(stdout)
215+ f.write(report)
216 machine.logger.info('Test log copied to ' + locallog)
217 locallogs.append(locallog)
218 if args.dumplogs:
219 with open(locallog, 'r') as f:
220 print(f.read())
221
222- return exitstatus
223-
224- exitstatuses = [_run(runlist) for runlist in args.runlists]
225- exitstatus = int(any(exitstatuses))
226-
227- if args.files is not None:
228- try:
229- locallogs += getfiles(args, machine)
230- except Exception as err:
231- machine.logger.warning('Failed to download files: ' + str(err))
232+ # Since we're running from a script, output INFO
233+ for logger in [machine.logger] + machine.logger.handlers:
234+ if logger.level > logging.INFO:
235+ logger.setLevel(logging.INFO)
236+
237+ # Write the machine name to standard out for log gathering
238+ print('Running on machine: ' + machine.name)
239+
240+ if args.json:
241+ extraopts = ' -f json'
242+ report_type = 'json'
243+
244+ else:
245+ extraopts = ''
246+ report_type = 'yaml'
247+ extraopts += " --install-type " + machine.installtype
248+
249+ locallogs = []
250+ try:
251+ machine.installclient()
252+ except Exception:
253+ traceback.print_exc()
254+ machine.logger.error('Failed to install client. '
255+ 'Writing empty reports.')
256+
257+ # Make sure that default logs are written
258+ for runlist in args.runlists:
259+ _write(runlist)
260+ exitstatus = 1
261+ else:
262+ # Server will return success code only if the execution
263+ # of every runlist was successful
264+ exitstatuses = [_run(runlist) for runlist in args.runlists]
265+ exitstatus = int(any(exitstatuses))
266+
267+ if args.files is not None:
268+ try:
269+ locallogs += getfiles(args, machine)
270+ except Exception as err:
271+ machine.logger.warning('Failed to download files: ' + str(err))
272
273 return exitstatus, locallogs
274

Subscribers

People subscribed via source and target branches