blob: 0ed7cc905e68dd228d442f1216dd2e88650e31a1 [file] [log] [blame]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001#!/usr/bin/env python3
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002
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
Patrick Williamsc0f7c042017-02-23 20:41:17 -060010import urllib.request, urllib.error
Patrick Williamsc124f4f2015-09-15 14:41:29 -050011import sys
12import json
13import os
14import subprocess
15import argparse
16import logging
17
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050018scripts_lib_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lib')
19sys.path.insert(0, scripts_lib_path)
20import argparse_oe
21
Patrick Williamsc124f4f2015-09-15 14:41:29 -050022version = "0.3"
23
24log = logging.getLogger("send-error-report")
25logging.basicConfig(format='%(levelname)s: %(message)s')
26
27def getPayloadLimit(url):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060028 req = urllib.request.Request(url, None)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -060030 response = urllib.request.urlopen(req)
31 except urllib.error.URLError as e:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050032 # Use this opportunity to bail out if we can't even contact the server
33 log.error("Could not contact server: " + url)
34 log.error(e.reason)
35 sys.exit(1)
36 try:
37 ret = json.loads(response.read())
38 max_log_size = ret.get('max_log_size', 0)
39 return int(max_log_size)
40 except:
41 pass
42
43 return 0
44
45def ask_for_contactdetails():
46 print("Please enter your name and your email (optionally), they'll be saved in the file you send.")
Patrick Williamsc0f7c042017-02-23 20:41:17 -060047 username = input("Name (required): ")
48 email = input("E-mail (not required): ")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050049 return username, email
50
51def edit_content(json_file_path):
Patrick Williamsc0f7c042017-02-23 20:41:17 -060052 edit = input("Review information before sending? (y/n): ")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050053 if 'y' in edit or 'Y' in edit:
54 editor = os.environ.get('EDITOR', None)
55 if editor:
56 subprocess.check_call([editor, json_file_path])
57 else:
58 log.error("Please set your EDITOR value")
59 sys.exit(1)
60 return True
61 return False
62
63def prepare_data(args):
64 # attempt to get the max_log_size from the server's settings
Brad Bishopf8caae32019-03-25 13:13:56 -040065 max_log_size = getPayloadLimit(args.protocol+args.server+"/ClientPost/JSON")
Patrick Williamsc124f4f2015-09-15 14:41:29 -050066
67 if not os.path.isfile(args.error_file):
68 log.error("No data file found.")
69 sys.exit(1)
70
71 home = os.path.expanduser("~")
72 userfile = os.path.join(home, ".oe-send-error")
73
74 try:
75 with open(userfile, 'r') as userfile_fp:
76 if len(args.name) == 0:
77 args.name = userfile_fp.readline()
78 else:
79 #use empty readline to increment the fp
80 userfile_fp.readline()
81
82 if len(args.email) == 0:
83 args.email = userfile_fp.readline()
84 except:
85 pass
86
87 if args.assume_yes == True and len(args.name) == 0:
88 log.error("Name needs to be provided either via "+userfile+" or as an argument (-n).")
89 sys.exit(1)
90
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080091 while len(args.name) <= 0 or len(args.name) > 50:
Patrick Williamsc124f4f2015-09-15 14:41:29 -050092 print("\nName needs to be given and must not more than 50 characters.")
93 args.name, args.email = ask_for_contactdetails()
94
95 with open(userfile, 'w') as userfile_fp:
96 userfile_fp.write(args.name.strip() + "\n")
97 userfile_fp.write(args.email.strip() + "\n")
98
99 with open(args.error_file, 'r') as json_fp:
100 data = json_fp.read()
101
102 jsondata = json.loads(data)
103 jsondata['username'] = args.name.strip()
104 jsondata['email'] = args.email.strip()
105 jsondata['link_back'] = args.link_back.strip()
106 # If we got a max_log_size then use this to truncate to get the last
107 # max_log_size bytes from the end
108 if max_log_size != 0:
109 for fail in jsondata['failures']:
110 if len(fail['log']) > max_log_size:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600111 print("Truncating log to allow for upload")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500112 fail['log'] = fail['log'][-max_log_size:]
113
114 data = json.dumps(jsondata, indent=4, sort_keys=True)
115
116 # Write back the result which will contain all fields filled in and
117 # any post processing done on the log data
118 with open(args.error_file, "w") as json_fp:
119 if data:
120 json_fp.write(data)
121
122
123 if args.assume_yes == False and edit_content(args.error_file):
124 #We'll need to re-read the content if we edited it
125 with open(args.error_file, 'r') as json_fp:
126 data = json_fp.read()
127
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600128 return data.encode('utf-8')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500129
130
131def send_data(data, args):
132 headers={'Content-type': 'application/json', 'User-Agent': "send-error-report/"+version}
133
134 if args.json:
Brad Bishopf8caae32019-03-25 13:13:56 -0400135 url = args.protocol+args.server+"/ClientPost/JSON/"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500136 else:
Brad Bishopf8caae32019-03-25 13:13:56 -0400137 url = args.protocol+args.server+"/ClientPost/"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500138
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600139 req = urllib.request.Request(url, data=data, headers=headers)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500140 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600141 response = urllib.request.urlopen(req)
142 except urllib.error.HTTPError as e:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800143 logging.error(str(e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500144 sys.exit(1)
145
Brad Bishopd5ae7d92018-06-14 09:52:03 -0700146 print(response.read().decode('utf-8'))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500147
148
149if __name__ == '__main__':
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500150 arg_parse = argparse_oe.ArgumentParser(description="This scripts will send an error report to your specified error-report-web server.")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500151
152 arg_parse.add_argument("error_file",
153 help="Generated error report file location",
154 type=str)
155
156 arg_parse.add_argument("-y",
157 "--assume-yes",
158 help="Assume yes to all queries and do not prompt",
159 action="store_true")
160
161 arg_parse.add_argument("-s",
162 "--server",
163 help="Server to send error report to",
164 type=str,
165 default="errors.yoctoproject.org")
166
167 arg_parse.add_argument("-e",
168 "--email",
169 help="Email address to be used for contact",
170 type=str,
171 default="")
172
173 arg_parse.add_argument("-n",
174 "--name",
175 help="Submitter name used to identify your error report",
176 type=str,
177 default="")
178
179 arg_parse.add_argument("-l",
180 "--link-back",
181 help="A url to link back to this build from the error report server",
182 type=str,
183 default="")
184
185 arg_parse.add_argument("-j",
186 "--json",
187 help="Return the result in json format, silences all other output",
188 action="store_true")
189
Brad Bishopf8caae32019-03-25 13:13:56 -0400190 arg_parse.add_argument("--no-ssl",
191 help="Use http instead of https protocol",
192 dest="protocol",
193 action="store_const", const="http://", default="https://")
194
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195
196
197 args = arg_parse.parse_args()
198
199 if (args.json == False):
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600200 print("Preparing to send errors to: "+args.server)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201
202 data = prepare_data(args)
203 send_data(data, args)
204
205 sys.exit(0)