Merge ubuntu-security-tools:umt_adt_results into ubuntu-security-tools:master

Proposed by Leonidas S. Barbosa
Status: Merged
Merged at revision: d62ac7996fe93988a27600f1c0581f8ab5138d27
Proposed branch: ubuntu-security-tools:umt_adt_results
Merge into: ubuntu-security-tools:master
Diff against target: 295 lines (+256/-0)
1 file modified
build-tools/umt (+256/-0)
Reviewer Review Type Date Requested Status
Steve Beattie Approve
Review via email: mp+404029@code.launchpad.net

Commit message

Adding umt adt tool in order to check autopkg tests results

Description of the change

This commit/branch adds a new tool to umt, umt adt in order to check the results from autopkgtests on britney locally.
It adds a default way to check it, in txt on terminal or saving a file: umt adt , umt adt --only-regressions or umt adt with --verbose (show in the terminal)
OR umt adt --html, saving a html file and opening it on the user browsers.

To post a comment you must log in.
Revision history for this message
Steve Beattie (sbeattie) wrote :

Thanks, this all looks good to me, merging. We can fine-tune the output as we get more experience with it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/build-tools/umt b/build-tools/umt
2index 243d16c..fdea237 100755
3--- a/build-tools/umt
4+++ b/build-tools/umt
5@@ -27,6 +27,7 @@ import json
6 import yaml
7 import threading
8 import lpl_common
9+import webbrowser
10 from collections import namedtuple
11
12 BinaryPackages = collections.namedtuple('BinaryPackages', 'binaries pkg_versions')
13@@ -46,6 +47,7 @@ coverity_dest = '../coverity'
14 qrt_dest = '../qrt'
15 autopkgtest_dest = '../autopkgtest'
16 testflinger_dest = '../testflinger'
17+adt_dest = '../adt'
18
19 # Per-package overrides for the sbuild resolver
20 # <sbuild resolver> = sbuild_dep_resolver_overrides[<srcpkg>][<ubuntu release>]
21@@ -1648,6 +1650,258 @@ def cmd_upload():
22 print("Destination is now: %s" % destination)
23 upload_changes_file(changes_file, destination, confirm=not opt.yes)
24
25+
26+class ADTHTML:
27+ html_output ="""
28+<html>
29+<head>
30+ <style>
31+ table {
32+ border: 1px solid black;
33+ width: 100%%;
34+ }
35+ tr:nth-child(even) {
36+ background-color: #f2f2f2;
37+ }
38+ </style>
39+</head>
40+<body>
41+<table>
42+<tr>
43+ <td></td>
44+ <td>amd64</td>
45+ <td>arm64</td>
46+ <td>armhf</td>
47+ <td>i386</td>
48+ <td>ppc64el</td>
49+ <td> s390x</td>
50+</tr>
51+%s
52+<tbody>
53+"""
54+ def __init__(self):
55+ self.lines = ""
56+ self.columns = ""
57+
58+ def add_line(self, line):
59+ self.lines += line
60+
61+ def add_column(self, column, start=False):
62+ if start:
63+ self.columns = column
64+ else:
65+ self.columns += column
66+
67+ def create_html(self, package, excuses_data, arch_list, only_regressions=False):
68+ count_regressions = 0
69+ for pkg in excuses_data[package]:
70+ pkg_name = list(pkg.keys())[0]
71+ self.add_column("<td>{0}</td>".format(pkg_name), True)
72+ had_regression = False
73+ for arch in arch_list:
74+ if arch not in pkg[pkg_name].keys():
75+ self.add_column("<td>NO ARCH</td>")
76+ continue
77+
78+ if pkg[pkg_name][arch][0] == 'REGRESSION':
79+ had_regression = True
80+ count_regressions += 1
81+
82+ self.add_column("<td><a href='{0}'>{1}</a></td>".format(
83+ pkg[pkg_name][arch][1], pkg[pkg_name][arch][0]))
84+ else:
85+ self.add_column("<td>{0}</td>".format(pkg[pkg_name][arch][0]))
86+
87+ if only_regressions:
88+ if had_regression:
89+ self.add_line("<tr>{0}</tr>\n".format(self.columns))
90+ had_regression = False
91+ else:
92+ self.add_line("<tr>{0}</tr>\n".format(self.columns))
93+
94+
95+ if only_regressions and count_regressions == 0:
96+ return None
97+
98+ return (self.html_output % self.lines, count_regressions)
99+
100+
101+def adt_text(excuses, package, arch_list, only_regressions=False, verbose=False):
102+ regressions_count = 0
103+ regressions_total = 0
104+ success_count = 0
105+ out_txt_lines = ""
106+ total_packages = 0
107+ if package in excuses.keys():
108+ total_packages = len(excuses[package])
109+ for pkg in excuses[package]:
110+ out_txt = ""
111+ excuse_pkg = list(pkg.keys())[0]
112+ regression = False
113+ for arch in arch_list:
114+ if arch in pkg[excuse_pkg].keys():
115+ arch_status = pkg[excuse_pkg][arch][0]
116+ if arch_status == 'REGRESSION':
117+ regressions_count += 1
118+ regression = True
119+ arch_status_format = colorise("red", arch_status)
120+ elif arch_status == 'PASS':
121+ arch_status_format = colorise("green", arch_status)
122+ elif arch_status in ['RUNNING', 'RUNNING-ALWAYSFAIL']:
123+ arch_status_format = colorise("yellow", arch_status)
124+ else:
125+ arch_status_format = arch_status
126+
127+ out_txt += "| {0} - {1} ".format(arch, arch_status_format)
128+
129+ if not regressions_count:
130+ success_count += 1
131+
132+ pkg_out = "{0}".format(excuse_pkg)
133+ if verbose:
134+ if only_regressions:
135+ if regression:
136+ warn(pkg_out)
137+ print(out_txt)
138+ else:
139+ if regression:
140+ warn(pkg_out)
141+ else:
142+ print(pkg_out)
143+ print(out_txt)
144+ else:
145+ if only_regressions:
146+ if regression:
147+ out_txt_lines += pkg_out + '\n'
148+ out_txt_lines += out_txt + '\n'
149+ else:
150+ out_txt_lines += pkg_out + '\n'
151+ out_txt_lines += out_txt + '\n'
152+
153+ regressions_total += regressions_count
154+ regressions_count = 0
155+
156+ return regressions_total, success_count, total_packages, out_txt_lines
157+
158+
159+def check_adt_results(excuses, package, html=False, only_regressions=False,
160+ verbose=False):
161+ excuses_status = ['PASS', 'RUNNING', 'ALWAYSFAIL', 'NEUTRAL', 'REGRESSION',
162+ 'RUNNING-ALWAYSFAIL', 'REJECTED_PERMANENTLY']
163+
164+ package_excuses = None
165+ # { <pkg_name>: [{'<pkg_tested': {'amd64': (<res>, <link_if_regression>), 'armh64..},]
166+ excuses_data = {package: []}
167+ arch_list = ['amd64', 'arm64', 'armhf', 'i386', 'ppc64el', 's390x']
168+
169+ for source in excuses['sources']:
170+ if source['source'] == package:
171+ package_excuses = source
172+ break
173+
174+ if package_excuses:
175+ autopkgtests = package_excuses['policy_info']['autopkgtest']
176+ for pkg in autopkgtests.keys():
177+ if not isinstance(autopkgtests[pkg], dict):
178+ continue
179+
180+ archs = {arch: (autopkgtests[pkg][arch][0], autopkgtests[pkg][arch][1]) \
181+ for arch in arch_list if arch in autopkgtests[pkg].keys()
182+ and autopkgtests[pkg][arch][0] in excuses_status
183+ }
184+
185+ excuses_data[package].append({pkg: archs})
186+
187+ if excuses_data[package]:
188+ if html:
189+ print("Checking autopkgtests results for {} ...".format(package))
190+ adt_html = ADTHTML()
191+ res, regress = adt_html.create_html(package, excuses_data, arch_list,
192+ only_regressions)
193+ if res == None:
194+ warn("packages related to {0} autopkg tests has no regressions".format(package))
195+ else:
196+ result_msg = "{regress} REGRESSIONS found".format(regress=regress)
197+ if regress:
198+ err(result_msg)
199+ else:
200+ success(result_msg)
201+
202+ print("opening ../adt/adt_results.html in the browser ...")
203+ html_file = "{0}/adt_results.html".format(adt_dest)
204+ with open(html_file, 'w') as f:
205+ f.write(res)
206+
207+ path = os.path.abspath(html_file)
208+ url = 'file://' + path
209+ webbrowser.open(url)
210+
211+ else:
212+ res, succ, total, out_txt = adt_text(excuses_data, package, arch_list,
213+ only_regressions, verbose)
214+ total_msg = "TOTAL Packages triggered: {total}".format(total=total)
215+ total_succ = "PACKAGES SUCCESS: {succs}".format(succs=succ)
216+ result_msg = "{res} REGRESSIONS found".format(res=res)
217+
218+ if not verbose:
219+ if res:
220+ print("saving ../adt/adt_results.txt ...")
221+ txt_file = "{0}/adt_results.txt".format(adt_dest)
222+ with open(txt_file, 'w') as f:
223+ f.write(out_txt)
224+
225+ print(total_msg)
226+ print(total_succ)
227+ if res:
228+ err(result_msg)
229+ else:
230+ success(result_msg)
231+
232+
233+# Usage, e.g: umt adt --only-regressions --html - shows only those had
234+# regression, and shows in the html format/opening in the browser
235+# umt adt - shows in text saving to ../adt/adt_results.txt if no --verbose is
236+# given. Same to umt adt --only-regression and with --verbose
237+def cmd_adt():
238+ excuses_url = "https://people.canonical.com/~platform/security-britney/current/"
239+ details = parse_package_details(skip_sanity = True)
240+ release = details['release']
241+ valid_releases = []
242+ for rel in source_map.cve_lib.releases:
243+ if source_map.cve_lib.is_active_release(rel) or source_map.cve_lib.is_active_esm_release(rel):
244+ valid_releases.append(rel)
245+
246+ parser = umt_optparse("usage: %prog adt [options]")
247+
248+ parser.add_option("--only-regressions", default=False, action='store_true',
249+ help="shows only packages that had a regression")
250+ parser.add_option("--verbose", default=False, action='store_true',
251+ help="shows output in terminal as verbose")
252+ parser.add_option("--html", default=False, action='store_true',
253+ help="saves a html output to ../adt and open it in a browser")
254+
255+ (opt, args) = parser.parse_args()
256+
257+ if release and release in valid_releases:
258+ excuses_yaml = "security_{0}_excuses.yaml".format(release)
259+ prepare_dir(adt_dest, True)
260+
261+ print("Getting {0} file ...".format(excuses_yaml))
262+ req = requests.get("{0}{1}".format(excuses_url, excuses_yaml))
263+ if not req.status_code == 200:
264+ err("failed to get the excuses yaml page: %s" % req.url, file=sys.stderr)
265+ req.raise_for_status()
266+ sys.exit(1)
267+
268+ excuses_yaml = req.content.decode()
269+ excuses = yaml.safe_load(excuses_yaml)
270+ check_adt_results(excuses, details['package'], opt.html,
271+ opt.only_regressions, opt.verbose)
272+
273+ else:
274+ err("Not a valid release")
275+
276+
277 def cmd_grep():
278 '''List source packages matching regex'''
279 parser = umt_optparse("usage: %prog grep regex")
280@@ -4295,6 +4549,7 @@ Uncomplicated Massive Tool (umt)
281 umt COMMAND [OPTIONS]
282
283 COMMAND:
284+adt Shows autopackage test comparison for the current release package
285 grep Find source packages by regex / pattern search
286 search List best source packages for each release
287 download Get source packages and unpack
288@@ -4362,6 +4617,7 @@ else:
289
290 # Command dispatch:
291 commands = {
292+ 'adt' : cmd_adt,
293 'grep' : cmd_grep,
294 'search' : cmd_search,
295 'download' : cmd_download,

Subscribers

People subscribed via source and target branches