poky/scripts/send-error-report
Changqing Li 58785be56a send-error-report: make output align with original design when debug disabled
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>
2025-05-08 10:28:19 +01:00

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)