
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>
7.5 KiB
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)