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
=== modified file 'examples/utah_syslog.sh'
--- examples/utah_syslog.sh 2012-07-31 14:22:31 +0000
+++ examples/utah_syslog.sh 2012-11-30 14:52:20 +0000
@@ -2,5 +2,5 @@
22
3for MACHINE in $@3for MACHINE in $@
4do4do
5 cat /var/log/utah/$(echo "$MACHINE" | grep -o "utah-[0-9]*")-*.syslog.log5 cat /var/log/utah/$(echo "$MACHINE" | grep -o "utah-[0-9]\+")-*.syslog.log
6done6done
77
=== modified file 'utah/iso.py'
--- utah/iso.py 2012-11-16 19:27:27 +0000
+++ utah/iso.py 2012-11-30 14:52:20 +0000
@@ -8,6 +8,7 @@
8import subprocess8import subprocess
9import sys9import sys
10import urllib10import urllib
11import re
1112
12from hashlib import md513from hashlib import md5
1314
@@ -62,8 +63,10 @@
62 # Ubuntu 12.10 "Quantal Quetzal" - Alpha amd64 (20120820)63 # Ubuntu 12.10 "Quantal Quetzal" - Alpha amd64 (20120820)
63 # i.e. Ubuntu Version (LTS) "Series Codename" - Alpha/Beta/Release64 # i.e. Ubuntu Version (LTS) "Series Codename" - Alpha/Beta/Release
64 # arch (buildnumber)65 # arch (buildnumber)
66 self.media_info = self.dump(self.infofile).communicate()[0]
65 self.arch = self.getarch()67 self.arch = self.getarch()
66 self.series = self.getseries()68 self.series = self.getseries()
69 self.buildnumber = self.getbuildnumber()
6770
68 def _loggersetup(self):71 def _loggersetup(self):
69 """72 """
@@ -118,8 +121,7 @@
118 """121 """
119 Unpack the image's info file to get the arch.122 Unpack the image's info file to get the arch.
120 """123 """
121 stdout = self.dump(self.infofile).communicate()[0]124 arch = self.media_info.split()[-2]
122 arch = stdout.split()[-2]
123 self.logger.info('Arch is: ' + arch)125 self.logger.info('Arch is: ' + arch)
124 return arch126 return arch
125127
@@ -127,14 +129,19 @@
127 """129 """
128 Unpack the image's info file to get the series.130 Unpack the image's info file to get the series.
129 """131 """
130 stdout = self.dump(self.infofile).communicate()[0]132 for word in self.media_info.split():
131 for word in stdout.split():
132 if word.startswith('"'):133 if word.startswith('"'):
133 series = word.strip('"').lower()134 series = word.strip('"').lower()
134 break135 break
135 self.logger.info('Series is ' + series)136 self.logger.info('Series is ' + series)
136 return series137 return series
137138
139 def getbuildnumber(self):
140 """Unpack the image's info file to get the build number."""
141 match = re.search('.*\(([0-9.]+)\)$', self.media_info)
142 build_number = match.group(1) if match else '?'
143 return build_number
144
138 def dldisplay(self, blocks, size, total):145 def dldisplay(self, blocks, size, total):
139 """146 """
140 Display download information.147 Display download information.
141148
=== modified file 'utah/parser.py'
--- utah/parser.py 2012-11-30 09:33:04 +0000
+++ utah/parser.py 2012-11-30 14:52:20 +0000
@@ -40,6 +40,10 @@
40 'type': 'string',40 'type': 'string',
41 'required': True,41 'required': True,
42 },42 },
43 'ran_at': {
44 'type': 'string',
45 'required': True,
46 },
43 'commands': {47 'commands': {
44 'type': 'array',48 'type': 'array',
45 'items': {'type': COMMAND_SCHEMA},49 'items': {'type': COMMAND_SCHEMA},
@@ -74,6 +78,18 @@
74 'type': 'string',78 'type': 'string',
75 'required': True,79 'required': True,
76 },80 },
81 'arch': {
82 'type': 'string',
83 'required': True,
84 },
85 'release': {
86 'type': 'string',
87 'required': True,
88 },
89 'build_number': {
90 'type': 'string',
91 'required': True,
92 },
77 },93 },
78 }94 }
7995
8096
=== modified file 'utah/run.py'
--- utah/run.py 2012-11-29 16:37:23 +0000
+++ utah/run.py 2012-11-30 14:52:20 +0000
@@ -5,6 +5,9 @@
5import os5import os
6import time6import time
7import urllib7import urllib
8import json
9import yaml
10import traceback
811
9from utah import config12from utah import config
10from utah.exceptions import UTAHException13from utah.exceptions import UTAHException
@@ -14,68 +17,120 @@
14 """17 """
15 Run some tests and retrieve results.18 Run some tests and retrieve results.
16 """19 """
17 # Since we're running from a script, output INFO
18 for logger in [machine.logger] + machine.logger.handlers:
19 if logger.level > logging.INFO:
20 logger.setLevel(logging.INFO)
21
22 # Write the machine name to standard out for log gathering
23 print('Running on machine: ' + machine.name)
24
25 if args.json:
26 extraopts = ' -f json'
27 suffix = 'json'
28 else:
29 extraopts = ''
30 suffix = 'yaml'
31
32 locallogs = []
33
34 machine.installclient()
35
36 extraopts += " --install-type " + machine.installtype
37
38 def _run(runlist):20 def _run(runlist):
39 """Run a single runlist."""21 """Run a single runlist."""
40 locallist = urllib.urlretrieve(runlist)[0]22 stdout = None
41 machine.uploadfiles([locallist], os.path.normpath('/tmp'))
4223
43 options = ' -r /tmp/' + os.path.basename(locallist)
44 utah_command = 'utah' + extraopts + options
45 try:24 try:
46 # Server will return same code as client25 locallist = urllib.urlretrieve(runlist)[0]
26 machine.uploadfiles([locallist], os.path.normpath('/tmp'))
27
28 options = ' -r /tmp/' + os.path.basename(locallist)
29 utah_command = 'utah' + extraopts + options
47 exitstatus, stdout, _stderr = machine.run(utah_command, root=True)30 exitstatus, stdout, _stderr = machine.run(utah_command, root=True)
48 except UTAHException as error:31 except UTAHException as error:
49 machine.logger.error('Failed to run test: ' + str(error))32 machine.logger.error('Failed to run test: ' + str(error))
50 return 133 exitstatus = 1
34
35 _write(runlist, stdout)
36
37 return exitstatus
38
39 def _write(runlist, report=None):
40 """Write runlist report.
41
42 Report will be written using appropriate filename based on the runlist.
43 If report is None becase there was some problem, then a default empty
44 report will be generated.
45
46 """
47 timestamp = time.strftime('%Y-%m-%d_%H-%m-%S', time.gmtime())
48
49 if report is None:
50 # Generate default report with empty results to fake client output
51 default_report = {
52 'runlist': runlist,
53 'media-info': machine.image.media_info,
54 'build_number': machine.image.buildnumber,
55 'release': machine.image.series,
56 'ran_at': timestamp,
57 'install_type': machine.installtype,
58 'arch': machine.arch,
59 'commands': [],
60 'fetch_errors': 0,
61 'errors': 0,
62 'failures': 0,
63 'passes': 0,
64 'uname': '',
65
66
67 }
68 if report_type == 'json':
69 report = json.dumps(default_report)
70 elif report_type == 'yaml':
71 report = yaml.dump(default_report,
72 explicit_start='---',
73 default_flow_style=False)
74 else:
75 raise ValueError('Unexpected report type: {}'
76 .format(report_type))
5177
52 listname = os.path.basename(runlist)78 listname = os.path.basename(runlist)
53 timestamp = time.strftime('%Y-%m-%d_%H-%m-%S', time.gmtime())
54 log_filename = ('{machine}_{runlist}_{timestamp}.{suffix}'79 log_filename = ('{machine}_{runlist}_{timestamp}.{suffix}'
55 .format(machine=machine.name,80 .format(machine=machine.name,
56 runlist=listname,81 runlist=listname,
57 timestamp=timestamp,82 timestamp=timestamp,
58 suffix=suffix))83 suffix=report_type))
59 locallog = os.path.join(config.logpath, log_filename)84 locallog = os.path.join(config.logpath, log_filename)
60 locallog = os.path.normpath(locallog)85 locallog = os.path.normpath(locallog)
61 with open(locallog, 'w') as f:86 with open(locallog, 'w') as f:
62 f.write(stdout)87 f.write(report)
63 machine.logger.info('Test log copied to ' + locallog)88 machine.logger.info('Test log copied to ' + locallog)
64 locallogs.append(locallog)89 locallogs.append(locallog)
65 if args.dumplogs:90 if args.dumplogs:
66 with open(locallog, 'r') as f:91 with open(locallog, 'r') as f:
67 print(f.read())92 print(f.read())
6893
69 return exitstatus94 # Since we're running from a script, output INFO
7095 for logger in [machine.logger] + machine.logger.handlers:
71 exitstatuses = [_run(runlist) for runlist in args.runlists]96 if logger.level > logging.INFO:
72 exitstatus = int(any(exitstatuses))97 logger.setLevel(logging.INFO)
7398
74 if args.files is not None:99 # Write the machine name to standard out for log gathering
75 try:100 print('Running on machine: ' + machine.name)
76 locallogs += getfiles(args, machine)101
77 except Exception as err:102 if args.json:
78 machine.logger.warning('Failed to download files: ' + str(err))103 extraopts = ' -f json'
104 report_type = 'json'
105
106 else:
107 extraopts = ''
108 report_type = 'yaml'
109 extraopts += " --install-type " + machine.installtype
110
111 locallogs = []
112 try:
113 machine.installclient()
114 except Exception:
115 traceback.print_exc()
116 machine.logger.error('Failed to install client. '
117 'Writing empty reports.')
118
119 # Make sure that default logs are written
120 for runlist in args.runlists:
121 _write(runlist)
122 exitstatus = 1
123 else:
124 # Server will return success code only if the execution
125 # of every runlist was successful
126 exitstatuses = [_run(runlist) for runlist in args.runlists]
127 exitstatus = int(any(exitstatuses))
128
129 if args.files is not None:
130 try:
131 locallogs += getfiles(args, machine)
132 except Exception as err:
133 machine.logger.warning('Failed to download files: ' + str(err))
79134
80 return exitstatus, locallogs135 return exitstatus, locallogs
81136

Subscribers

People subscribed via source and target branches