1eb8dc403SDave Cobbley#!/usr/bin/env python3 2eb8dc403SDave Cobbley 3eb8dc403SDave Cobbley# Sends an error report (if the report-error class was enabled) to a 4eb8dc403SDave Cobbley# remote server. 5eb8dc403SDave Cobbley# 6eb8dc403SDave Cobbley# Copyright (C) 2013 Intel Corporation 7eb8dc403SDave Cobbley# Author: Andreea Proca <andreea.b.proca@intel.com> 8eb8dc403SDave Cobbley# Author: Michael Wood <michael.g.wood@intel.com> 9*c342db35SBrad Bishop# 10*c342db35SBrad Bishop# SPDX-License-Identifier: GPL-2.0-only 11*c342db35SBrad Bishop# 12eb8dc403SDave Cobbley 13eb8dc403SDave Cobbleyimport urllib.request, urllib.error 14eb8dc403SDave Cobbleyimport sys 15eb8dc403SDave Cobbleyimport json 16eb8dc403SDave Cobbleyimport os 17eb8dc403SDave Cobbleyimport subprocess 18eb8dc403SDave Cobbleyimport argparse 19eb8dc403SDave Cobbleyimport logging 20eb8dc403SDave Cobbley 21eb8dc403SDave Cobbleyscripts_lib_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib') 22eb8dc403SDave Cobbleysys.path.insert(0, scripts_lib_path) 23eb8dc403SDave Cobbleyimport argparse_oe 24eb8dc403SDave Cobbley 25eb8dc403SDave Cobbleyversion = "0.3" 26eb8dc403SDave Cobbley 27eb8dc403SDave Cobbleylog = logging.getLogger("send-error-report") 28eb8dc403SDave Cobbleylogging.basicConfig(format='%(levelname)s: %(message)s') 29eb8dc403SDave Cobbley 30eb8dc403SDave Cobbleydef getPayloadLimit(url): 31eb8dc403SDave Cobbley req = urllib.request.Request(url, None) 32eb8dc403SDave Cobbley try: 33eb8dc403SDave Cobbley response = urllib.request.urlopen(req) 34eb8dc403SDave Cobbley except urllib.error.URLError as e: 35eb8dc403SDave Cobbley # Use this opportunity to bail out if we can't even contact the server 36eb8dc403SDave Cobbley log.error("Could not contact server: " + url) 37eb8dc403SDave Cobbley log.error(e.reason) 38eb8dc403SDave Cobbley sys.exit(1) 39eb8dc403SDave Cobbley try: 40eb8dc403SDave Cobbley ret = json.loads(response.read()) 41eb8dc403SDave Cobbley max_log_size = ret.get('max_log_size', 0) 42eb8dc403SDave Cobbley return int(max_log_size) 43eb8dc403SDave Cobbley except: 44eb8dc403SDave Cobbley pass 45eb8dc403SDave Cobbley 46eb8dc403SDave Cobbley return 0 47eb8dc403SDave Cobbley 48eb8dc403SDave Cobbleydef ask_for_contactdetails(): 49eb8dc403SDave Cobbley print("Please enter your name and your email (optionally), they'll be saved in the file you send.") 50eb8dc403SDave Cobbley username = input("Name (required): ") 51eb8dc403SDave Cobbley email = input("E-mail (not required): ") 52eb8dc403SDave Cobbley return username, email 53eb8dc403SDave Cobbley 54eb8dc403SDave Cobbleydef edit_content(json_file_path): 55eb8dc403SDave Cobbley edit = input("Review information before sending? (y/n): ") 56eb8dc403SDave Cobbley if 'y' in edit or 'Y' in edit: 57eb8dc403SDave Cobbley editor = os.environ.get('EDITOR', None) 58eb8dc403SDave Cobbley if editor: 59eb8dc403SDave Cobbley subprocess.check_call([editor, json_file_path]) 60eb8dc403SDave Cobbley else: 61eb8dc403SDave Cobbley log.error("Please set your EDITOR value") 62eb8dc403SDave Cobbley sys.exit(1) 63eb8dc403SDave Cobbley return True 64eb8dc403SDave Cobbley return False 65eb8dc403SDave Cobbley 66eb8dc403SDave Cobbleydef prepare_data(args): 67eb8dc403SDave Cobbley # attempt to get the max_log_size from the server's settings 68f8caae30SBrad Bishop max_log_size = getPayloadLimit(args.protocol+args.server+"/ClientPost/JSON") 69eb8dc403SDave Cobbley 70eb8dc403SDave Cobbley if not os.path.isfile(args.error_file): 71eb8dc403SDave Cobbley log.error("No data file found.") 72eb8dc403SDave Cobbley sys.exit(1) 73eb8dc403SDave Cobbley 74eb8dc403SDave Cobbley home = os.path.expanduser("~") 75eb8dc403SDave Cobbley userfile = os.path.join(home, ".oe-send-error") 76eb8dc403SDave Cobbley 77eb8dc403SDave Cobbley try: 78eb8dc403SDave Cobbley with open(userfile, 'r') as userfile_fp: 79eb8dc403SDave Cobbley if len(args.name) == 0: 80eb8dc403SDave Cobbley args.name = userfile_fp.readline() 81eb8dc403SDave Cobbley else: 82eb8dc403SDave Cobbley #use empty readline to increment the fp 83eb8dc403SDave Cobbley userfile_fp.readline() 84eb8dc403SDave Cobbley 85eb8dc403SDave Cobbley if len(args.email) == 0: 86eb8dc403SDave Cobbley args.email = userfile_fp.readline() 87eb8dc403SDave Cobbley except: 88eb8dc403SDave Cobbley pass 89eb8dc403SDave Cobbley 90eb8dc403SDave Cobbley if args.assume_yes == True and len(args.name) == 0: 91eb8dc403SDave Cobbley log.error("Name needs to be provided either via "+userfile+" or as an argument (-n).") 92eb8dc403SDave Cobbley sys.exit(1) 93eb8dc403SDave Cobbley 941a4b7ee2SBrad Bishop while len(args.name) <= 0 or len(args.name) > 50: 95eb8dc403SDave Cobbley print("\nName needs to be given and must not more than 50 characters.") 96eb8dc403SDave Cobbley args.name, args.email = ask_for_contactdetails() 97eb8dc403SDave Cobbley 98eb8dc403SDave Cobbley with open(userfile, 'w') as userfile_fp: 99eb8dc403SDave Cobbley userfile_fp.write(args.name.strip() + "\n") 100eb8dc403SDave Cobbley userfile_fp.write(args.email.strip() + "\n") 101eb8dc403SDave Cobbley 102eb8dc403SDave Cobbley with open(args.error_file, 'r') as json_fp: 103eb8dc403SDave Cobbley data = json_fp.read() 104eb8dc403SDave Cobbley 105eb8dc403SDave Cobbley jsondata = json.loads(data) 106eb8dc403SDave Cobbley jsondata['username'] = args.name.strip() 107eb8dc403SDave Cobbley jsondata['email'] = args.email.strip() 108eb8dc403SDave Cobbley jsondata['link_back'] = args.link_back.strip() 109eb8dc403SDave Cobbley # If we got a max_log_size then use this to truncate to get the last 110eb8dc403SDave Cobbley # max_log_size bytes from the end 111eb8dc403SDave Cobbley if max_log_size != 0: 112eb8dc403SDave Cobbley for fail in jsondata['failures']: 113eb8dc403SDave Cobbley if len(fail['log']) > max_log_size: 114eb8dc403SDave Cobbley print("Truncating log to allow for upload") 115eb8dc403SDave Cobbley fail['log'] = fail['log'][-max_log_size:] 116eb8dc403SDave Cobbley 117eb8dc403SDave Cobbley data = json.dumps(jsondata, indent=4, sort_keys=True) 118eb8dc403SDave Cobbley 119eb8dc403SDave Cobbley # Write back the result which will contain all fields filled in and 120eb8dc403SDave Cobbley # any post processing done on the log data 121eb8dc403SDave Cobbley with open(args.error_file, "w") as json_fp: 122eb8dc403SDave Cobbley if data: 123eb8dc403SDave Cobbley json_fp.write(data) 124eb8dc403SDave Cobbley 125eb8dc403SDave Cobbley 126eb8dc403SDave Cobbley if args.assume_yes == False and edit_content(args.error_file): 127eb8dc403SDave Cobbley #We'll need to re-read the content if we edited it 128eb8dc403SDave Cobbley with open(args.error_file, 'r') as json_fp: 129eb8dc403SDave Cobbley data = json_fp.read() 130eb8dc403SDave Cobbley 131eb8dc403SDave Cobbley return data.encode('utf-8') 132eb8dc403SDave Cobbley 133eb8dc403SDave Cobbley 134eb8dc403SDave Cobbleydef send_data(data, args): 135eb8dc403SDave Cobbley headers={'Content-type': 'application/json', 'User-Agent': "send-error-report/"+version} 136eb8dc403SDave Cobbley 137eb8dc403SDave Cobbley if args.json: 138f8caae30SBrad Bishop url = args.protocol+args.server+"/ClientPost/JSON/" 139eb8dc403SDave Cobbley else: 140f8caae30SBrad Bishop url = args.protocol+args.server+"/ClientPost/" 141eb8dc403SDave Cobbley 142eb8dc403SDave Cobbley req = urllib.request.Request(url, data=data, headers=headers) 143eb8dc403SDave Cobbley try: 144eb8dc403SDave Cobbley response = urllib.request.urlopen(req) 145eb8dc403SDave Cobbley except urllib.error.HTTPError as e: 1461a4b7ee2SBrad Bishop logging.error(str(e)) 147eb8dc403SDave Cobbley sys.exit(1) 148eb8dc403SDave Cobbley 149d5ae7d90SBrad Bishop print(response.read().decode('utf-8')) 150eb8dc403SDave Cobbley 151eb8dc403SDave Cobbley 152eb8dc403SDave Cobbleyif __name__ == '__main__': 153eb8dc403SDave Cobbley arg_parse = argparse_oe.ArgumentParser(description="This scripts will send an error report to your specified error-report-web server.") 154eb8dc403SDave Cobbley 155eb8dc403SDave Cobbley arg_parse.add_argument("error_file", 156eb8dc403SDave Cobbley help="Generated error report file location", 157eb8dc403SDave Cobbley type=str) 158eb8dc403SDave Cobbley 159eb8dc403SDave Cobbley arg_parse.add_argument("-y", 160eb8dc403SDave Cobbley "--assume-yes", 161eb8dc403SDave Cobbley help="Assume yes to all queries and do not prompt", 162eb8dc403SDave Cobbley action="store_true") 163eb8dc403SDave Cobbley 164eb8dc403SDave Cobbley arg_parse.add_argument("-s", 165eb8dc403SDave Cobbley "--server", 166eb8dc403SDave Cobbley help="Server to send error report to", 167eb8dc403SDave Cobbley type=str, 168eb8dc403SDave Cobbley default="errors.yoctoproject.org") 169eb8dc403SDave Cobbley 170eb8dc403SDave Cobbley arg_parse.add_argument("-e", 171eb8dc403SDave Cobbley "--email", 172eb8dc403SDave Cobbley help="Email address to be used for contact", 173eb8dc403SDave Cobbley type=str, 174eb8dc403SDave Cobbley default="") 175eb8dc403SDave Cobbley 176eb8dc403SDave Cobbley arg_parse.add_argument("-n", 177eb8dc403SDave Cobbley "--name", 178eb8dc403SDave Cobbley help="Submitter name used to identify your error report", 179eb8dc403SDave Cobbley type=str, 180eb8dc403SDave Cobbley default="") 181eb8dc403SDave Cobbley 182eb8dc403SDave Cobbley arg_parse.add_argument("-l", 183eb8dc403SDave Cobbley "--link-back", 184eb8dc403SDave Cobbley help="A url to link back to this build from the error report server", 185eb8dc403SDave Cobbley type=str, 186eb8dc403SDave Cobbley default="") 187eb8dc403SDave Cobbley 188eb8dc403SDave Cobbley arg_parse.add_argument("-j", 189eb8dc403SDave Cobbley "--json", 190eb8dc403SDave Cobbley help="Return the result in json format, silences all other output", 191eb8dc403SDave Cobbley action="store_true") 192eb8dc403SDave Cobbley 193f8caae30SBrad Bishop arg_parse.add_argument("--no-ssl", 194f8caae30SBrad Bishop help="Use http instead of https protocol", 195f8caae30SBrad Bishop dest="protocol", 196f8caae30SBrad Bishop action="store_const", const="http://", default="https://") 197f8caae30SBrad Bishop 198eb8dc403SDave Cobbley 199eb8dc403SDave Cobbley 200eb8dc403SDave Cobbley args = arg_parse.parse_args() 201eb8dc403SDave Cobbley 202eb8dc403SDave Cobbley if (args.json == False): 203eb8dc403SDave Cobbley print("Preparing to send errors to: "+args.server) 204eb8dc403SDave Cobbley 205eb8dc403SDave Cobbley data = prepare_data(args) 206eb8dc403SDave Cobbley send_data(data, args) 207eb8dc403SDave Cobbley 208eb8dc403SDave Cobbley sys.exit(0) 209