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