send-error-report 6.7 KB

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