diff options
-rwxr-xr-x | scripts/send-error-report | 274 |
1 files changed, 178 insertions, 96 deletions
diff --git a/scripts/send-error-report b/scripts/send-error-report index 01c292ead1..1a1b96580d 100755 --- a/scripts/send-error-report +++ b/scripts/send-error-report | |||
@@ -1,114 +1,196 @@ | |||
1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
2 | 2 | ||
3 | # Sends an error report (if the report-error class was enabled) to a remote server. | 3 | # Sends an error report (if the report-error class was enabled) to a |
4 | # remote server. | ||
4 | # | 5 | # |
5 | # Copyright (C) 2013 Intel Corporation | 6 | # Copyright (C) 2013 Intel Corporation |
6 | # Author: Andreea Proca <andreea.b.proca@intel.com> | 7 | # Author: Andreea Proca <andreea.b.proca@intel.com> |
8 | # Author: Michael Wood <michael.g.wood@intel.com> | ||
9 | |||
10 | import urllib2 | ||
11 | import sys | ||
12 | import json | ||
13 | import os | ||
14 | import subprocess | ||
15 | import argparse | ||
16 | import logging | ||
17 | |||
18 | version = "0.3" | ||
19 | |||
20 | log = logging.getLogger("send-error-report") | ||
21 | logging.basicConfig(format='%(levelname)s: %(message)s') | ||
22 | |||
23 | def getPayloadLimit(url): | ||
24 | req = urllib2.Request(url, None) | ||
25 | try: | ||
26 | response = urllib2.urlopen(req) | ||
27 | except urllib2.URLError as e: | ||
28 | # Use this opportunity to bail out if we can't even contact the server | ||
29 | log.error("Could not contact server: " + url) | ||
30 | log.error(e.reason) | ||
31 | sys.exit(1) | ||
32 | try: | ||
33 | ret = json.loads(response.read()) | ||
34 | max_log_size = ret.get('max_log_size', 0) | ||
35 | return int(max_log_size) | ||
36 | except: | ||
37 | pass | ||
38 | |||
39 | return 0 | ||
40 | |||
41 | def ask_for_contactdetails(): | ||
42 | print("Please enter your name and your email (optionally), they'll be saved in the file you send.") | ||
43 | username = raw_input("Name (required): ") | ||
44 | email = raw_input("E-mail (not required): ") | ||
45 | return username, email | ||
46 | |||
47 | def edit_content(json_file_path): | ||
48 | edit = raw_input("Review information before sending? (y/n): ") | ||
49 | if 'y' in edit or 'Y' in edit: | ||
50 | editor = os.environ.get('EDITOR', None) | ||
51 | if editor: | ||
52 | subprocess.check_call([editor, json_file_path]) | ||
53 | else: | ||
54 | log.error("Please set your EDITOR value") | ||
55 | sys.exit(1) | ||
56 | return True | ||
57 | return False | ||
7 | 58 | ||
59 | def prepare_data(args): | ||
60 | # attempt to get the max_log_size from the server's settings | ||
61 | max_log_size = getPayloadLimit("http://"+args.server+"/ClientPost/JSON") | ||
8 | 62 | ||
63 | if not os.path.isfile(args.error_file): | ||
64 | log.error("No data file found.") | ||
65 | sys.exit(1) | ||
9 | 66 | ||
10 | import httplib, urllib, os, sys, json, base64 | 67 | home = os.path.expanduser("~") |
11 | from urllib2 import _parse_proxy as parseproxy | 68 | userfile = os.path.join(home, ".oe-send-error") |
12 | |||
13 | |||
14 | def handle_connection(server, data): | ||
15 | params = urllib.urlencode({'data': data}) | ||
16 | headers = {"Content-type": "application/json"} | ||
17 | proxyrequired = False | ||
18 | if os.environ.get("http_proxy") or os.environ.get("HTTP_PROXY"): | ||
19 | proxyrequired = True | ||
20 | # we need to check that the server isn't a local one, as in no_proxy | ||
21 | try: | ||
22 | temp = httplib.HTTPConnection(server, strict=True, timeout=5) | ||
23 | temp.request("GET", "/Errors/") | ||
24 | tempres = temp.getresponse() | ||
25 | if tempres.status == 200: | ||
26 | proxyrequired = False | ||
27 | temp.close() | ||
28 | except: | ||
29 | pass | ||
30 | |||
31 | if proxyrequired: | ||
32 | proxy = parseproxy(os.environ.get("http_proxy") or os.environ.get("HTTP_PROXY")) | ||
33 | if proxy[1] and proxy[2]: | ||
34 | auth = base64.encodestring("%s:%s" % (proxy[1], proxy[2])) | ||
35 | headers["Authorization"] = "Basic %s" % auth | ||
36 | conn = httplib.HTTPConnection(proxy[3]) | ||
37 | conn.request("POST", "http://%s/ClientPost/" % server, params, headers) | ||
38 | else: | ||
39 | conn = httplib.HTTPConnection(server) | ||
40 | conn.request("POST", "/ClientPost/", params, headers) | ||
41 | 69 | ||
42 | return conn | 70 | try: |
71 | with open(userfile, 'r') as userfile_fp: | ||
72 | if len(args.name) == 0: | ||
73 | args.name = userfile_fp.readline() | ||
74 | else: | ||
75 | #use empty readline to increment the fp | ||
76 | userfile_fp.readline() | ||
43 | 77 | ||
78 | if len(args.email) == 0: | ||
79 | args.email = userfile_fp.readline() | ||
80 | except: | ||
81 | pass | ||
44 | 82 | ||
45 | def sendData(json_file, server): | 83 | if args.assume_yes == True and len(args.name) == 0: |
84 | log.error("Name needs to be provided either via "+userfile+" or as an argument (-n).") | ||
85 | sys.exit(1) | ||
46 | 86 | ||
47 | if os.path.isfile(json_file): | 87 | while len(args.name) <= 0 and len(args.name) < 50: |
88 | print("\nName needs to be given and must not more than 50 characters.") | ||
89 | args.name, args.email = ask_for_contactdetails() | ||
48 | 90 | ||
49 | home = os.path.expanduser("~") | 91 | with open(userfile, 'w') as userfile_fp: |
50 | userfile = os.path.join(home, ".oe-send-error") | 92 | userfile_fp.write(args.name.strip() + "\n") |
51 | if os.path.isfile(userfile): | 93 | userfile_fp.write(args.email.strip() + "\n") |
52 | with open(userfile) as g: | 94 | |
53 | username = g.readline() | 95 | with open(args.error_file, 'r') as json_fp: |
54 | email = g.readline() | 96 | data = json_fp.read() |
55 | else: | 97 | |
56 | print("Please enter your name and your email (optionally), they'll be saved in the file you send.") | 98 | jsondata = json.loads(data) |
57 | username = raw_input("Name: ") | 99 | jsondata['username'] = args.name.strip() |
58 | email = raw_input("E-mail (not required): ") | 100 | jsondata['email'] = args.email.strip() |
59 | if len(username) > 0 and len(username) < 50: | 101 | jsondata['link_back'] = args.link_back.strip() |
60 | with open(userfile, "w") as g: | 102 | # If we got a max_log_size then use this to truncate to get the last |
61 | g.write(username + "\n") | 103 | # max_log_size bytes from the end |
62 | g.write(email + "\n") | 104 | if max_log_size != 0: |
63 | else: | 105 | for fail in jsondata['failures']: |
64 | print("Invalid inputs, try again.") | 106 | if len(fail['log']) > max_log_size: |
65 | sys.exit(1) | 107 | print "Truncating log to allow for upload" |
66 | return | 108 | fail['log'] = fail['log'][-max_log_size:] |
67 | 109 | ||
68 | with open(json_file) as f: | 110 | data = json.dumps(jsondata, indent=4, sort_keys=True) |
69 | data = f.read() | 111 | |
70 | 112 | # Write back the result which will contain all fields filled in and | |
71 | try: | 113 | # any post processing done on the log data |
72 | jsondata = json.loads(data) | 114 | with open(args.error_file, "w") as json_fp: |
73 | jsondata['username'] = username.strip() | 115 | if data: |
74 | jsondata['email'] = email.strip() | 116 | json_fp.write(data) |
75 | data = json.dumps(jsondata, indent=4, sort_keys=True) | 117 | |
76 | except: | 118 | |
77 | print("Invalid json data") | 119 | if args.assume_yes == False and edit_content(args.error_file): |
78 | sys.exit(1) | 120 | #We'll need to re-read the content if we edited it |
79 | return | 121 | with open(args.error_file, 'r') as json_fp: |
80 | 122 | data = json_fp.read() | |
81 | try: | 123 | |
82 | conn = handle_connection(server, data) | 124 | return data |
83 | response = conn.getresponse() | 125 | |
84 | print response.status, response.reason | 126 | |
85 | res = response.read() | 127 | def send_data(data, args): |
86 | if response.status == 200: | 128 | headers={'Content-type': 'application/json', 'User-Agent': "send-error-report/"+version} |
87 | print(res) | 129 | |
88 | else: | 130 | if args.json: |
89 | print("There was a problem submiting your data, response written in %s.response.html" % json_file) | 131 | url = "http://"+args.server+"/ClientPost/JSON/" |
90 | with open("%s.response.html" % json_file, "w") as f: | ||
91 | f.write(res) | ||
92 | sys.exit(1) | ||
93 | conn.close() | ||
94 | except Exception as e: | ||
95 | print("Server connection failed: %s" % e) | ||
96 | sys.exit(1) | ||
97 | else: | 132 | else: |
98 | print("No data file found.") | 133 | url = "http://"+args.server+"/ClientPost/" |
134 | |||
135 | req = urllib2.Request(url, data=data, headers=headers) | ||
136 | try: | ||
137 | response = urllib2.urlopen(req) | ||
138 | except urllib2.HTTPError, e: | ||
139 | logging.error(e.reason) | ||
99 | sys.exit(1) | 140 | sys.exit(1) |
100 | 141 | ||
142 | print response.read() | ||
143 | |||
101 | 144 | ||
102 | if __name__ == '__main__': | 145 | if __name__ == '__main__': |
103 | print ("\nSends an error report (if the report-error class was enabled) to a remote server.") | 146 | arg_parse = argparse.ArgumentParser(description="This scripts will send an error report to your specified error-report-web server.") |
104 | print("\nThis scripts sends the contents of the error to a public upstream server.") | 147 | |
105 | print("\nPlease remove any identifying information before sending.") | 148 | arg_parse.add_argument("error_file", |
106 | if len(sys.argv) < 2: | 149 | help="Generated error report file location", |
107 | print("\nUsage: send-error-report <error_fileName> [server]") | 150 | type=str) |
108 | print("\nIf this is the first when sending a report you'll be asked for your name and optionally your email address.") | 151 | |
109 | print("They will be associated with your report.\n") | 152 | arg_parse.add_argument("-y", |
110 | 153 | "--assume-yes", | |
111 | elif len(sys.argv) == 3: | 154 | help="Assume yes to all queries and do not prompt", |
112 | sendData(sys.argv[1], sys.argv[2]) | 155 | action="store_true") |
113 | else: | 156 | |
114 | sendData(sys.argv[1], "errors.yoctoproject.org") | 157 | arg_parse.add_argument("-s", |
158 | "--server", | ||
159 | help="Server to send error report to", | ||
160 | type=str, | ||
161 | default="errors.yoctoproject.org") | ||
162 | |||
163 | arg_parse.add_argument("-e", | ||
164 | "--email", | ||
165 | help="Email address to be used for contact", | ||
166 | type=str, | ||
167 | default="") | ||
168 | |||
169 | arg_parse.add_argument("-n", | ||
170 | "--name", | ||
171 | help="Submitter name used to identify your error report", | ||
172 | type=str, | ||
173 | default="") | ||
174 | |||
175 | arg_parse.add_argument("-l", | ||
176 | "--link-back", | ||
177 | help="A url to link back to this build from the error report server", | ||
178 | type=str, | ||
179 | default="") | ||
180 | |||
181 | arg_parse.add_argument("-j", | ||
182 | "--json", | ||
183 | help="Return the result in json format, silences all other output", | ||
184 | action="store_true") | ||
185 | |||
186 | |||
187 | |||
188 | args = arg_parse.parse_args() | ||
189 | |||
190 | if (args.json == False): | ||
191 | print "Preparing to send errors to: "+args.server | ||
192 | |||
193 | data = prepare_data(args) | ||
194 | send_data(data, args) | ||
195 | |||
196 | sys.exit(0) | ||