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