refs #10700 - donate-cpu-server.py: added query parameter `pkgs` to some reports to request a list of affected packages (#3743)

This commit is contained in:
Oliver Stöneberg 2022-09-29 21:55:44 +02:00 committed by GitHub
parent 083efe6361
commit 0ab7116891
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 38 deletions

View File

@ -148,6 +148,13 @@ jobs:
env:
PYTHONPATH: ./tools
- name: test donate_cpu_server
if: matrix.python-version != '2.7'
run: |
python -m pytest -v tools/test_donate_cpu_server.py
env:
PYTHONPATH: ./tools
- name: dmake
if: matrix.python-version == '3.10'
run: |

View File

@ -21,11 +21,12 @@ import logging
import logging.handlers
import operator
import html as html_lib
from urllib.parse import urlparse
# Version scheme (MAJOR.MINOR.PATCH) should orientate on "Semantic Versioning" https://semver.org/
# Every change in this script should result in increasing the version number accordingly (exceptions may be cosmetic
# changes)
SERVER_VERSION = "1.3.29"
SERVER_VERSION = "1.3.30"
OLD_VERSION = '2.9'
@ -42,12 +43,13 @@ if logfile:
logfile += '/'
logfile += 'donate-cpu-server.log'
handler_file = logging.handlers.RotatingFileHandler(filename=logfile, maxBytes=100*1024, backupCount=1)
handler_file.setFormatter(logging.Formatter('%(asctime)s %(message)s'))
handler_file.setLevel(logging.ERROR)
logger.addHandler(handler_file)
def print_ts(msg):
print('[' + strDateTime() + '] ' + msg)
def print_ts(msg) -> None:
print('[{}] {}'.format(strDateTime(), msg))
# Set up an exception hook for all uncaught exceptions so they can be logged
@ -179,7 +181,9 @@ def latestReport(latestResults: list) -> str:
return html
def crashReport(results_path: str) -> str:
def crashReport(results_path: str, query_params: dict):
pkgs = '' if query_params.get('pkgs') == '1' else None
html = '<html><head><title>Crash report</title></head><body>\n'
html += '<h1>Crash report</h1>\n'
html += '<pre>\n'
@ -191,6 +195,7 @@ def crashReport(results_path: str) -> str:
continue
with open(filename, 'rt') as file_:
datestr = None
package_url = None
for line in file_:
line = line.strip()
if line.startswith('cppcheck: '):
@ -202,6 +207,8 @@ def crashReport(results_path: str) -> str:
continue
if datestr is None and line.startswith(str(current_year) + '-') or line.startswith(str(current_year - 1) + '-'):
datestr = line
elif pkgs is not None and package_url is None and line.startswith('ftp://'):
package_url = line
elif line.startswith('count:'):
if line.find('Crash') < 0:
break
@ -214,6 +221,8 @@ def crashReport(results_path: str) -> str:
if counts[1] == 'Crash!':
c_head = 'Crash'
html += fmt(package, datestr, c_version, c_head) + '\n'
if package_url is not None:
pkgs += '{}\n'.format(package_url)
if c_head != 'Crash':
break
elif line.find(' received signal ') != -1:
@ -271,7 +280,9 @@ def crashReport(results_path: str) -> str:
html += '</pre>\n'
html += '</body></html>\n'
return html
if pkgs is not None:
return pkgs, 'text/plain'
return html, 'text/html'
def timeoutReport(results_path: str) -> str:
@ -609,7 +620,8 @@ def headReport(resultsPath: str) -> str:
return html
def headMessageIdReport(resultPath: str, messageId: str) -> str:
def headMessageIdReport(resultPath: str, messageId: str, query_params: dict) -> str:
pkgs = '' if query_params.get('pkgs') == '1' else None
text = messageId + '\n'
e = '[' + messageId + ']\n'
for filename in sorted(glob.glob(resultPath + '/*')):
@ -629,8 +641,12 @@ def headMessageIdReport(resultPath: str, messageId: str) -> str:
elif line.endswith(e):
if url:
text += url
if pkgs is not None:
pkgs += url
url = None
text += line
if pkgs is not None:
return pkgs
return text
@ -938,83 +954,91 @@ class HttpClientThread(Thread):
def __init__(self, connection: socket.socket, cmd: str, resultPath: str, latestResults: list) -> None:
Thread.__init__(self)
self.connection = connection
self.cmd = cmd[:cmd.find('\n')]
self.cmd = cmd[:cmd.find('\r\n')]
self.resultPath = resultPath
self.latestResults = latestResults
# TODO: use a proper parser
def parse_req(cmd):
req_parts = cmd.split(' ')
if len(req_parts) != 3 or req_parts[0] != 'GET' or not req_parts[2].startswith('HTTP'):
return None, None
url_obj = urlparse(req_parts[1])
return url_obj.path, dict(urllib.parse.parse_qsl(url_obj.query))
def run(self):
try:
cmd = self.cmd
print_ts(cmd)
res = re.match(r'GET /([a-zA-Z0-9_\-\.\+%]*) HTTP', cmd)
if res is None:
url, queryParams = HttpClientThread.parse_req(cmd)
if url is None:
print_ts('invalid request: {}'.format(cmd))
self.connection.close()
return
url = res.group(1)
if url == '':
if url == '/':
html = overviewReport()
httpGetResponse(self.connection, html, 'text/html')
elif url == 'latest.html':
elif url == '/latest.html':
html = latestReport(self.latestResults)
httpGetResponse(self.connection, html, 'text/html')
elif url == 'crash.html':
html = crashReport(self.resultPath)
httpGetResponse(self.connection, html, 'text/html')
elif url == 'timeout.html':
elif url == '/crash.html':
text, mime = crashReport(self.resultPath, queryParams)
httpGetResponse(self.connection, text, mime)
elif url == '/timeout.html':
html = timeoutReport(self.resultPath)
httpGetResponse(self.connection, html, 'text/html')
elif url == 'stale.html':
elif url == '/stale.html':
html = staleReport(self.resultPath)
httpGetResponse(self.connection, html, 'text/html')
elif url == 'diff.html':
elif url == '/diff.html':
html = diffReport(self.resultPath)
httpGetResponse(self.connection, html, 'text/html')
elif url.startswith('difftoday-'):
messageId = url[10:]
elif url.startswith('/difftoday-'):
messageId = url[len('/difftoday-'):]
text = diffMessageIdTodayReport(self.resultPath, messageId)
httpGetResponse(self.connection, text, 'text/plain')
elif url.startswith('diff-'):
messageId = url[5:]
elif url.startswith('/diff-'):
messageId = url[len('/diff-'):]
text = diffMessageIdReport(self.resultPath, messageId)
httpGetResponse(self.connection, text, 'text/plain')
elif url == 'head.html':
elif url == '/head.html':
html = headReport(self.resultPath)
httpGetResponse(self.connection, html, 'text/html')
elif url.startswith('headtoday-'):
messageId = url[10:]
elif url.startswith('/headtoday-'):
messageId = url[len('/headtoday-'):]
text = headMessageIdTodayReport(self.resultPath, messageId)
httpGetResponse(self.connection, text, 'text/plain')
elif url.startswith('head-'):
messageId = url[5:]
text = headMessageIdReport(self.resultPath, messageId)
elif url.startswith('/head-'):
messageId = url[len('/head-'):]
text = headMessageIdReport(self.resultPath, messageId, queryParams)
httpGetResponse(self.connection, text, 'text/plain')
elif url == 'time_lt.html':
elif url == '/time_lt.html':
text = timeReport(self.resultPath, False)
httpGetResponse(self.connection, text, 'text/html')
elif url == 'time_gt.html':
elif url == '/time_gt.html':
text = timeReport(self.resultPath, True)
httpGetResponse(self.connection, text, 'text/html')
elif url == 'time_slow.html':
elif url == '/time_slow.html':
text = timeReportSlow(self.resultPath)
httpGetResponse(self.connection, text, 'text/html')
elif url == 'check_library_function_report.html':
elif url == '/check_library_function_report.html':
text = check_library_report(self.resultPath + '/' + 'info_output', message_id='checkLibraryFunction')
httpGetResponse(self.connection, text, 'text/html')
elif url == 'check_library_noreturn_report.html':
elif url == '/check_library_noreturn_report.html':
text = check_library_report(self.resultPath + '/' + 'info_output', message_id='checkLibraryNoReturn')
httpGetResponse(self.connection, text, 'text/html')
elif url == 'check_library_use_ignore_report.html':
elif url == '/check_library_use_ignore_report.html':
text = check_library_report(self.resultPath + '/' + 'info_output', message_id='checkLibraryUseIgnore')
httpGetResponse(self.connection, text, 'text/html')
elif url == 'check_library_check_type_report.html':
elif url == '/check_library_check_type_report.html':
text = check_library_report(self.resultPath + '/' + 'info_output', message_id='checkLibraryCheckType')
httpGetResponse(self.connection, text, 'text/html')
elif url.startswith('check_library-'):
function_name = url[len('check_library-'):]
elif url.startswith('/check_library-'):
function_name = url[len('/check_library-'):]
text = check_library_function_name(self.resultPath + '/' + 'info_output', function_name)
httpGetResponse(self.connection, text, 'text/plain')
else:
filename = resultPath + '/' + url
filename = resultPath + url
if not os.path.isfile(filename):
print_ts('HTTP/1.1 404 Not Found')
self.connection.send(b'HTTP/1.1 404 Not Found\r\n\r\n')

View File

@ -0,0 +1,17 @@
from importlib import import_module
donate_cpu_server = import_module('donate-cpu-server')
def _test_parse_req(req_str, url_exp, queryParams_exp):
url, queryParams = donate_cpu_server.HttpClientThread.parse_req(req_str)
assert url == url_exp and queryParams == queryParams_exp
def test_parse_req():
_test_parse_req("", None, None)
_test_parse_req("GET / HTTP/1.1", '/', {})
_test_parse_req("GET /crash.html HTTP/1.1", '/crash.html', {})
_test_parse_req("GET /head-uninitvar HTTP/1.1", '/head-uninitvar', {})
_test_parse_req("GET /check_library-std%3A%3Aunordered_set%3A%3Ainsert%28%29 HTTP/1.1", '/check_library-std%3A%3Aunordered_set%3A%3Ainsert%28%29', {})
_test_parse_req("GET /head-uninitvar?pkgs=1 HTTP/1.1", '/head-uninitvar', {'pkgs': '1'})
_test_parse_req("GET /crash.html?pkgs=1 HTTP/1.1", '/crash.html', {'pkgs': '1'})
_test_parse_req("GET /head-uninitvar?pkgs=1&pkgs2=2 HTTP/1.1", '/head-uninitvar', {'pkgs': '1', 'pkgs2': '2'})