mirror of
git://git.yoctoproject.org/poky.git
synced 2025-07-19 21:09:03 +02:00

First, it is better to output the error report web link by default when the report is uploaded successfully like before. This is useful, user can find the detail the log info from the return link. Yocto Autobuilder also benifit from this return link. Second, why don't set level to logging.INFO to make the error report web link outputed? Because "-j" option want to "Return the result in json format, silences all other output", So "INFO:" added by logging system is not wanted, so use print directly. Example output: Without "-j": Preparing to send errors to: http://x.x.x.x:8000 Your entry can be found here: http://x.x.x.x:8000/Errors/Build/25/ With "-j": {"build_id": 27, "build_url": "http://x.x.x.x:8000/Errors/Build/27/", "failures": [{"id": 26, "url": "http://x.x.x.x:8000/Errors/Details/26/"}], "num_similar_errors": 20, "similar_errors_url": "http://x.x.x.x:8000/Errors/SimilarTo/26/"} (From OE-Core rev: c45aca4592544d867f49055426e68dd338d4adcc) Signed-off-by: Changqing Li <changqing.li@windriver.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
232 lines
7.5 KiB
Python
Executable File
232 lines
7.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# Sends an error report (if the report-error class was enabled) to a
|
|
# remote server.
|
|
#
|
|
# Copyright (C) 2013 Intel Corporation
|
|
# Author: Andreea Proca <andreea.b.proca@intel.com>
|
|
# Author: Michael Wood <michael.g.wood@intel.com>
|
|
# Author: Thomas Perrot <thomas.perrot@bootlin.com>
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
#
|
|
|
|
import urllib.request, urllib.error
|
|
import sys
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import argparse
|
|
import logging
|
|
|
|
scripts_lib_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib')
|
|
sys.path.insert(0, scripts_lib_path)
|
|
import argparse_oe
|
|
|
|
version = "0.4"
|
|
|
|
log = logging.getLogger("send-error-report")
|
|
logging.basicConfig(format='%(levelname)s: %(message)s')
|
|
|
|
def getPayloadLimit(url):
|
|
req = urllib.request.Request(url, None)
|
|
try:
|
|
response = urllib.request.urlopen(req)
|
|
except urllib.error.URLError as e:
|
|
# Use this opportunity to bail out if we can't even contact the server
|
|
log.error("Could not contact server: " + url)
|
|
log.error(e.reason)
|
|
sys.exit(1)
|
|
try:
|
|
ret = json.loads(response.read())
|
|
max_log_size = ret.get('max_log_size', 0)
|
|
return int(max_log_size)
|
|
except:
|
|
pass
|
|
|
|
return 0
|
|
|
|
def ask_for_contactdetails():
|
|
print("Please enter your name and your email (optionally), they'll be saved in the file you send.")
|
|
username = input("Name (required): ")
|
|
email = input("E-mail (not required): ")
|
|
return username, email
|
|
|
|
def edit_content(json_file_path):
|
|
edit = input("Review information before sending? (y/n): ")
|
|
if 'y' in edit or 'Y' in edit:
|
|
editor = os.environ.get('EDITOR', None)
|
|
if editor:
|
|
subprocess.check_call([editor, json_file_path])
|
|
else:
|
|
log.error("Please set your EDITOR value")
|
|
sys.exit(1)
|
|
return True
|
|
return False
|
|
|
|
def prepare_data(args):
|
|
# attempt to get the max_log_size from the server's settings
|
|
max_log_size = getPayloadLimit(args.server+"/ClientPost/JSON")
|
|
|
|
if not os.path.isfile(args.error_file):
|
|
log.error("No data file found.")
|
|
sys.exit(1)
|
|
|
|
home = os.path.expanduser("~")
|
|
userfile = os.path.join(home, ".oe-send-error")
|
|
|
|
try:
|
|
with open(userfile, 'r') as userfile_fp:
|
|
if len(args.name) == 0:
|
|
args.name = userfile_fp.readline()
|
|
else:
|
|
#use empty readline to increment the fp
|
|
userfile_fp.readline()
|
|
|
|
if len(args.email) == 0:
|
|
args.email = userfile_fp.readline()
|
|
except:
|
|
pass
|
|
|
|
if args.assume_yes == True and len(args.name) == 0:
|
|
log.error("Name needs to be provided either via "+userfile+" or as an argument (-n).")
|
|
sys.exit(1)
|
|
|
|
while len(args.name) <= 0 or len(args.name) > 50:
|
|
print("\nName needs to be given and must not more than 50 characters.")
|
|
args.name, args.email = ask_for_contactdetails()
|
|
|
|
with open(userfile, 'w') as userfile_fp:
|
|
userfile_fp.write(args.name.strip() + "\n")
|
|
userfile_fp.write(args.email.strip() + "\n")
|
|
|
|
with open(args.error_file, 'r') as json_fp:
|
|
data = json_fp.read()
|
|
|
|
jsondata = json.loads(data)
|
|
jsondata['username'] = args.name.strip()
|
|
jsondata['email'] = args.email.strip()
|
|
jsondata['link_back'] = args.link_back.strip()
|
|
# If we got a max_log_size then use this to truncate to get the last
|
|
# max_log_size bytes from the end
|
|
if max_log_size != 0:
|
|
for fail in jsondata['failures']:
|
|
if len(fail['log']) > max_log_size:
|
|
print("Truncating log to allow for upload")
|
|
fail['log'] = fail['log'][-max_log_size:]
|
|
|
|
data = json.dumps(jsondata, indent=4, sort_keys=True)
|
|
|
|
# Write back the result which will contain all fields filled in and
|
|
# any post processing done on the log data
|
|
with open(args.error_file, "w") as json_fp:
|
|
if data:
|
|
json_fp.write(data)
|
|
|
|
|
|
if args.assume_yes == False and edit_content(args.error_file):
|
|
#We'll need to re-read the content if we edited it
|
|
with open(args.error_file, 'r') as json_fp:
|
|
data = json_fp.read()
|
|
|
|
return data.encode('utf-8')
|
|
|
|
|
|
def send_data(data, args):
|
|
headers={'Content-type': 'application/json', 'User-Agent': "send-error-report/"+version}
|
|
|
|
if args.json:
|
|
url = args.server+"/ClientPost/JSON/"
|
|
else:
|
|
url = args.server+"/ClientPost/"
|
|
|
|
req = urllib.request.Request(url, data=data, headers=headers)
|
|
|
|
log.debug(f"Request URL: {url}")
|
|
log.debug(f"Request Headers: {headers}")
|
|
log.debug(f"Request Data: {data.decode('utf-8')}")
|
|
|
|
try:
|
|
response = urllib.request.urlopen(req)
|
|
except urllib.error.HTTPError as e:
|
|
log.error(f"HTTP Error {e.code}: {e.reason}")
|
|
log.debug(f"Response Content: {e.read().decode('utf-8')}")
|
|
sys.exit(1)
|
|
|
|
log.debug(f"Response Status: {response.status}")
|
|
log.debug(f"Response Headers: {response.getheaders()}")
|
|
print(response.read().decode('utf-8'))
|
|
|
|
def validate_server_url(args):
|
|
# Get the error report server from an argument
|
|
server = args.server or 'https://errors.yoctoproject.org'
|
|
|
|
if not server.startswith('http://') and not server.startswith('https://'):
|
|
log.error("Missing a URL scheme either http:// or https:// in the server name: " + server)
|
|
sys.exit(1)
|
|
|
|
# Construct the final URL
|
|
return f"{server}"
|
|
|
|
|
|
if __name__ == '__main__':
|
|
arg_parse = argparse_oe.ArgumentParser(description="This scripts will send an error report to your specified error-report-web server.")
|
|
|
|
arg_parse.add_argument("error_file",
|
|
help="Generated error report file location",
|
|
type=str)
|
|
|
|
arg_parse.add_argument("-y",
|
|
"--assume-yes",
|
|
help="Assume yes to all queries and do not prompt",
|
|
action="store_true")
|
|
|
|
arg_parse.add_argument("-s",
|
|
"--server",
|
|
help="Server to send error report to",
|
|
type=str)
|
|
|
|
arg_parse.add_argument("-e",
|
|
"--email",
|
|
help="Email address to be used for contact",
|
|
type=str,
|
|
default="")
|
|
|
|
arg_parse.add_argument("-n",
|
|
"--name",
|
|
help="Submitter name used to identify your error report",
|
|
type=str,
|
|
default="")
|
|
|
|
arg_parse.add_argument("-l",
|
|
"--link-back",
|
|
help="A url to link back to this build from the error report server",
|
|
type=str,
|
|
default="")
|
|
|
|
arg_parse.add_argument("-j",
|
|
"--json",
|
|
help="Return the result in json format, silences all other output",
|
|
action="store_true")
|
|
|
|
arg_parse.add_argument("-d",
|
|
"--debug",
|
|
help="Enable debug mode to print request/response details",
|
|
action="store_true")
|
|
|
|
args = arg_parse.parse_args()
|
|
|
|
args.server = validate_server_url(args)
|
|
|
|
if (args.json == False):
|
|
print("Preparing to send errors to: "+args.server)
|
|
|
|
# Enable debugging if requested
|
|
if args.debug:
|
|
log.setLevel(logging.DEBUG)
|
|
|
|
data = prepare_data(args)
|
|
send_data(data, args)
|
|
|
|
sys.exit(0)
|