|
|
@ -28,6 +28,7 @@ import io
|
|
|
|
import itertools
|
|
|
|
import itertools
|
|
|
|
import re
|
|
|
|
import re
|
|
|
|
import socket
|
|
|
|
import socket
|
|
|
|
|
|
|
|
import sqlite3
|
|
|
|
import sys
|
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
import time
|
|
|
|
import urllib.parse
|
|
|
|
import urllib.parse
|
|
|
@ -124,7 +125,7 @@ class AutopkgtestPolicy(BasePolicy):
|
|
|
|
self.pending_tests_file = os.path.join(self.state_dir, 'autopkgtest-pending.json')
|
|
|
|
self.pending_tests_file = os.path.join(self.state_dir, 'autopkgtest-pending.json')
|
|
|
|
self.testsuite_triggers = {}
|
|
|
|
self.testsuite_triggers = {}
|
|
|
|
self.result_in_baseline_cache = collections.defaultdict(dict)
|
|
|
|
self.result_in_baseline_cache = collections.defaultdict(dict)
|
|
|
|
self.database = os.path.join(self.state_dir, 'autopkgtest.db')
|
|
|
|
self.database_path = os.path.join(self.state_dir, 'autopkgtest.db')
|
|
|
|
|
|
|
|
|
|
|
|
# results map: trigger -> src -> arch -> [passed, version, run_id, seen]
|
|
|
|
# results map: trigger -> src -> arch -> [passed, version, run_id, seen]
|
|
|
|
# - trigger is "source/version" of an unstable package that triggered
|
|
|
|
# - trigger is "source/version" of an unstable package that triggered
|
|
|
@ -145,6 +146,7 @@ class AutopkgtestPolicy(BasePolicy):
|
|
|
|
if not self.fetch_db():
|
|
|
|
if not self.fetch_db():
|
|
|
|
self.logger.error('No autopkgtest db present, exiting')
|
|
|
|
self.logger.error('No autopkgtest db present, exiting')
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
self.db = sqlite3.connect(self.database_path)
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
self.options.adt_ppas = self.options.adt_ppas.strip().split()
|
|
|
|
self.options.adt_ppas = self.options.adt_ppas.strip().split()
|
|
|
@ -170,7 +172,7 @@ class AutopkgtestPolicy(BasePolicy):
|
|
|
|
http_code = f.getcode()
|
|
|
|
http_code = f.getcode()
|
|
|
|
# file:/// urls don't have the http niceties
|
|
|
|
# file:/// urls don't have the http niceties
|
|
|
|
if not http_code or http_code == 200:
|
|
|
|
if not http_code or http_code == 200:
|
|
|
|
new_file = self.database + '.new'
|
|
|
|
new_file = self.database_path + '.new'
|
|
|
|
with open(new_file,'wb') as f_out:
|
|
|
|
with open(new_file,'wb') as f_out:
|
|
|
|
while True:
|
|
|
|
while True:
|
|
|
|
data=f.read(2048*1024)
|
|
|
|
data=f.read(2048*1024)
|
|
|
@ -181,7 +183,7 @@ class AutopkgtestPolicy(BasePolicy):
|
|
|
|
self.logger.info('Short read downloading autopkgtest results')
|
|
|
|
self.logger.info('Short read downloading autopkgtest results')
|
|
|
|
os.unlink(new_file)
|
|
|
|
os.unlink(new_file)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
os.rename(new_file, self.database)
|
|
|
|
os.rename(new_file, self.database_path)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self.logger.error('Failure to fetch autopkgtest results %s: HTTP code=%d', self.options.adt_db_url, f.getcode())
|
|
|
|
self.logger.error('Failure to fetch autopkgtest results %s: HTTP code=%d', self.options.adt_db_url, f.getcode())
|
|
|
|
except IOError as e:
|
|
|
|
except IOError as e:
|
|
|
@ -189,7 +191,7 @@ class AutopkgtestPolicy(BasePolicy):
|
|
|
|
finally:
|
|
|
|
finally:
|
|
|
|
if f is not None:
|
|
|
|
if f is not None:
|
|
|
|
f.close()
|
|
|
|
f.close()
|
|
|
|
return os.path.exists(self.database)
|
|
|
|
return os.path.exists(self.database_path)
|
|
|
|
|
|
|
|
|
|
|
|
def register_hints(self, hint_parser):
|
|
|
|
def register_hints(self, hint_parser):
|
|
|
|
hint_parser.register_hint_type('force-badtest', britney2.hints.split_into_one_hint_per_package)
|
|
|
|
hint_parser.register_hint_type('force-badtest', britney2.hints.split_into_one_hint_per_package)
|
|
|
@ -1004,6 +1006,71 @@ class AutopkgtestPolicy(BasePolicy):
|
|
|
|
for trigger in result_triggers:
|
|
|
|
for trigger in result_triggers:
|
|
|
|
self.add_trigger_to_results(trigger, src, ver, arch, run_id, seen, result)
|
|
|
|
self.add_trigger_to_results(trigger, src, ver, arch, run_id, seen, result)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fetch_sqlite_results(self, src, arch):
|
|
|
|
|
|
|
|
'''Retrieve new results for source package/arch from sqlite
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Remove matching pending_tests entries.
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# determine latest run_id from results
|
|
|
|
|
|
|
|
latest_run_id = ''
|
|
|
|
|
|
|
|
if not self.options.adt_shared_results_cache:
|
|
|
|
|
|
|
|
latest_run_id = self.latest_run_for_package(src, arch)
|
|
|
|
|
|
|
|
if not latest_run_id:
|
|
|
|
|
|
|
|
latest_run_id = ''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cur = self.db.cursor()
|
|
|
|
|
|
|
|
for row in cur.execute('SELECT r.exitcode,r.version,r.triggers,'
|
|
|
|
|
|
|
|
' r.run_id FROM test AS t '
|
|
|
|
|
|
|
|
'LEFT JOIN result AS r ON t.id=r.test_id '
|
|
|
|
|
|
|
|
'WHERE t.release=? AND t.arch=? '
|
|
|
|
|
|
|
|
'AND t.package=? AND r.run_id >= ?',
|
|
|
|
|
|
|
|
(self.options.series,arch, src, latest_run_id)):
|
|
|
|
|
|
|
|
exitcode, ver, triggers, run_id = row
|
|
|
|
|
|
|
|
if not ver:
|
|
|
|
|
|
|
|
if exitcode in (4, 12, 20):
|
|
|
|
|
|
|
|
# repair it
|
|
|
|
|
|
|
|
ver = "unknown"
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
self.logger.error('%s/%s/%s is damaged, ignoring',
|
|
|
|
|
|
|
|
arch, src, run_id)
|
|
|
|
|
|
|
|
# ignore this; this will leave an orphaned request
|
|
|
|
|
|
|
|
# in autopkgtest-pending.json and thus require
|
|
|
|
|
|
|
|
# manual retries after fixing the tmpfail, but we
|
|
|
|
|
|
|
|
# can't just blindly attribute it to some pending
|
|
|
|
|
|
|
|
# test.
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# parse recorded triggers in test result
|
|
|
|
|
|
|
|
if triggers:
|
|
|
|
|
|
|
|
result_triggers = [i for i in triggers.split(' ', 1) if '/' in i]
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
self.logger.error('%s result has no ADT_TEST_TRIGGERS, ignoring')
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 20200101_000000 is 15 chars long
|
|
|
|
|
|
|
|
seen = round(calendar.timegm(time.strptime(run_id[0:15], '%Y%m%d_%H%M%S')))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# allow some skipped tests, but nothing else
|
|
|
|
|
|
|
|
if exitcode in [0, 2]:
|
|
|
|
|
|
|
|
result = Result.PASS
|
|
|
|
|
|
|
|
elif exitcode == 8:
|
|
|
|
|
|
|
|
result = Result.NEUTRAL
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
result = Result.FAIL
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.logger.info(
|
|
|
|
|
|
|
|
'Fetched test result for %s/%s/%s %s (triggers: %s): %s',
|
|
|
|
|
|
|
|
src, ver, arch, run_id, result_triggers, result.name.lower())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# remove matching test requests
|
|
|
|
|
|
|
|
for trigger in result_triggers:
|
|
|
|
|
|
|
|
self.remove_from_pending(trigger, src, arch)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# add this result
|
|
|
|
|
|
|
|
for trigger in result_triggers:
|
|
|
|
|
|
|
|
self.add_trigger_to_results(trigger, src, ver, arch, run_id, seen, result)
|
|
|
|
|
|
|
|
|
|
|
|
def remove_from_pending(self, trigger, src, arch):
|
|
|
|
def remove_from_pending(self, trigger, src, arch):
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
arch_list = self.pending_tests[trigger][src]
|
|
|
|
arch_list = self.pending_tests[trigger][src]
|
|
|
@ -1105,17 +1172,21 @@ class AutopkgtestPolicy(BasePolicy):
|
|
|
|
result[3] + int(self.options.adt_retry_older_than) * SECPERDAY < self._now:
|
|
|
|
result[3] + int(self.options.adt_retry_older_than) * SECPERDAY < self._now:
|
|
|
|
# We might want to retry this failure, so continue
|
|
|
|
# We might want to retry this failure, so continue
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
elif not uses_swift:
|
|
|
|
elif not uses_swift and not hasattr(self,'db'):
|
|
|
|
# We're done if we don't retrigger and we're not using swift
|
|
|
|
# We're done if we don't retrigger and we're not using swift
|
|
|
|
return
|
|
|
|
return
|
|
|
|
elif result_state in {Result.PASS, Result.NEUTRAL}:
|
|
|
|
elif result_state in {Result.PASS, Result.NEUTRAL}:
|
|
|
|
self.logger.debug('%s/%s triggered by %s already known', src, arch, trigger)
|
|
|
|
self.logger.debug('%s/%s triggered by %s already known', src, arch, trigger)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# Without swift we don't expect new results
|
|
|
|
# Without swift or autopkgtest.db we don't expect new results
|
|
|
|
if uses_swift:
|
|
|
|
if hasattr(self,'db'):
|
|
|
|
|
|
|
|
self.fetch_sqlite_results(src, arch)
|
|
|
|
|
|
|
|
elif uses_swift:
|
|
|
|
self.logger.info('Checking for new results for failed %s/%s for trigger %s', src, arch, trigger)
|
|
|
|
self.logger.info('Checking for new results for failed %s/%s for trigger %s', src, arch, trigger)
|
|
|
|
self.fetch_swift_results(self.options.adt_swift_url, src, arch)
|
|
|
|
self.fetch_swift_results(self.options.adt_swift_url, src, arch)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if hasattr(self,'db') or uses_swift:
|
|
|
|
# do we have one now?
|
|
|
|
# do we have one now?
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
self.test_results[trigger][src][arch]
|
|
|
|
self.test_results[trigger][src][arch]
|
|
|
|