blob: 0d8a9b2b7fb5a79b4d7cf7d1cabd55ad0de6f461 [file] [log] [blame]
Dennis Kempin351024d2013-02-06 11:18:21 -08001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from gzip import GzipFile
6from StringIO import StringIO
7import base64
8import bz2
9import cookielib
10import getpass
11import os
12import os.path
13import re
14import subprocess
15import sys
16import tarfile
17import tempfile
18import urllib2
Dennis Kempinecd9a0c2013-03-27 16:03:46 -070019import zipfile
Dennis Kempin351024d2013-02-06 11:18:21 -080020
21# path to current script directory
22script_dir = os.path.dirname(os.path.realpath(__file__))
23
24# path to the cookies file used for storing the login cookies.
Dennis Kempin136b7322013-02-06 13:20:57 -080025cookies_file = os.path.join(script_dir, "mtedit.cookies")
Dennis Kempin351024d2013-02-06 11:18:21 -080026
27
28def Log(source, options):
29 """
30 Returns a log object containg activity, evdev and system logs.
31 source can be either an ip address to a device from which to
32 download, a url pointing to a feedback report to pull or a filename.
33 """
34 if os.path.exists(source):
Dennis Kempin91e96df2013-03-29 14:21:42 -070035 if source.endswith(".zip") or source.endswith(".bz2"):
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040036 return FeedbackLog(source, options)
Dennis Kempin351024d2013-02-06 11:18:21 -080037 return FileLog(source, options)
38 else:
39 match = re.search("Report/([0-9]+)", source)
40 if match:
41 return FeedbackLog(match.group(1), options)
42 else:
43 # todo add regex and error message
44 return DeviceLog(source, options)
45
46
47class AbstractLog(object):
48 """
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040049 A log file consists of the activity log, the evdev log, a system log, and
50 possibly a screenshot, which can be saved to disk all together.
Dennis Kempin351024d2013-02-06 11:18:21 -080051 """
52 def SaveAs(self, filename):
53 open(filename, "w").write(self.activity)
54 if self.evdev:
55 open(filename + ".evdev", "w").write(self.evdev)
56 if self.system:
57 open(filename + ".system", "w").write(self.system)
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040058 if self.image:
59 open(filename + ".jpg", "w").write(self.image)
60
61 def CleanUp(self):
62 if os.path.exists("screenshot.jpg"):
63 os.remove("screenshot.jpg")
Dennis Kempin351024d2013-02-06 11:18:21 -080064
65
66class FileLog(AbstractLog):
67 """
68 Loads log from file. Does not contain evdev or system logs.
69 """
70 def __init__(self, filename, options):
71 self.activity = open(filename).read()
72 self.evdev = ""
73 self.system = ""
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040074 self.image = None
Dennis Kempin7b783272013-04-01 15:06:35 -070075 if options.evdev:
76 self.evdev = open(options.evdev).read()
77 elif os.path.exists(filename + ".evdev"):
Dennis Kempinfa6597c2013-02-20 14:05:19 -080078 self.evdev = open(filename + ".evdev").read()
79 if os.path.exists(filename + ".system"):
80 self.system = open(filename + ".system").read()
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040081 if os.path.exists(filename + ".jpg"):
82 self.image = open(filename + ".jpg").read()
83 file("screenshot.jpg", "w").write(self.image)
Dennis Kempin351024d2013-02-06 11:18:21 -080084
85
86class FeedbackLog(AbstractLog):
87 """
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -040088 Downloads logs and (possibly) screenshot from a feedback id or file name
Dennis Kempin351024d2013-02-06 11:18:21 -080089 """
Dennis Kempin91e96df2013-03-29 14:21:42 -070090 def __init__(self, id_or_filename, options):
91 if id_or_filename.endswith(".zip") or id_or_filename.endswith(".bz2"):
92 self.report = open(id_or_filename).read()
93 else:
94 # setup cookies file and url opener then download the report archive
95 self.cookies = cookielib.MozillaCookieJar(cookies_file)
96 if os.path.exists(cookies_file):
97 self.cookies.load()
98 cookie_processor = urllib2.HTTPCookieProcessor(self.cookies)
99 opener = urllib2.build_opener(cookie_processor)
100 self._DownloadSystemLog(id_or_filename, opener)
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -0400101 self.image = None
102 if options.screenshot:
103 self._DownloadScreenshot(id_or_filename, opener)
104 # Only write to screenshot.jpg if we will be viewing the screenshot
105 if not options.download and self.image:
106 file("screenshot.jpg", "w").write(self.image)
Dennis Kempin351024d2013-02-06 11:18:21 -0800107
Dennis Kempin91e96df2013-03-29 14:21:42 -0700108 self._ExtractSystemLog()
Dennis Kempin351024d2013-02-06 11:18:21 -0800109 self._ExtractLogFiles()
110
111 def _Login(self, opener):
112 print "Login to corp.google.com:"
113 username = getpass.getuser()
114 password = getpass.getpass()
115 sys.stdout.write("OTP: ")
116 otp = sys.stdin.readline().strip()
117
118 # execute login by posting userdata to login.corp.google.com
119 url = "https://login.corp.google.com/login?ssoformat=CORP_SSO"
120 data = "u=%s&pw=%s&otp=%s" % (username, password, otp)
121 result = opener.open(url, data).read()
122
123 # check if the result displays error
124 if result.find("error") >= 0:
125 print "Login failed"
126 exit(-1)
127
128 # login was successful. save cookies to file to be reused later.
129 self.cookies.save()
130
131 def _GetLatestFile(self, match, tar):
132 # find file name containing match with latest timestamp
133 names = filter(lambda n: n.find(match) >= 0, tar.getnames())
134 names.sort()
Dennis Kempin765ad942013-03-26 13:58:46 -0700135 if not names:
136 print "Cannot find files named %s in tar file" % match
137 print "Tar file contents:", tar.getnames()
138 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800139 return tar.extractfile(names[-1])
140
141 def _DownloadSystemLog(self, id, opener):
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700142 # First download the report.zip file
143 logfile = "report-%s-system_logs.zip" % id
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -0400144 url = ("https://feedback.corp.googleusercontent.com/binarydata/" +
Dennis Kempin351024d2013-02-06 11:18:21 -0800145 "%s?id=%s&logIndex=0") % (logfile, id)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700146 self.report = opener.open(url).read()
147 file("test.zip", "w").write(self.report)
Dennis Kempin351024d2013-02-06 11:18:21 -0800148
Dennis Kempin91e96df2013-03-29 14:21:42 -0700149 if self.report[0:2] != "BZ" and self.report[0:2] != "PK":
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700150 # not a zip/bzip file. Log in and try again.
Dennis Kempin351024d2013-02-06 11:18:21 -0800151 self._Login(opener)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700152 self.report = opener.open(url).read()
Dennis Kempin351024d2013-02-06 11:18:21 -0800153
WeiNan-Peter, Wen9aab26a2013-05-13 09:32:29 -0400154 def _DownloadScreenshot(self, id, opener):
155 url = "https://feedback.corp.googleusercontent.com/screenshot?id=%s" % id
156 self.image = None
157 try:
158 self.image = opener.open(url).read()
159 except urllib2.URLError:
160 print "No screenshots available - %s" % url
161
Dennis Kempin91e96df2013-03-29 14:21:42 -0700162 def _ExtractSystemLog(self):
163 if self.report[0:2] == "BZ":
164 self.system = bz2.decompress(self.report)
165 elif self.report[0:2] == "PK":
166 io = StringIO(self.report)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700167 zip = zipfile.ZipFile(io, "r")
168 self.system = zip.read(zip.namelist()[0])
169 zip.extractall("./")
170 else:
171 print "Cannot download logs file"
172 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800173
174 def _ExtractLogFiles(self):
175 # find embedded and uuencoded activity.tar in system log
Dennis Kempin91e96df2013-03-29 14:21:42 -0700176 log_start1 = ("hack-33025-touchpad_activity=\"\"\"\n" +
Dennis Kempin351024d2013-02-06 11:18:21 -0800177 "begin-base64 644 touchpad_activity_log.tar\n")
Dennis Kempin91e96df2013-03-29 14:21:42 -0700178 log_start2 = ("touchpad_activity=\"\"\"\n" +
179 "begin-base64 644 touchpad_activity_log.tar\n")
180 log_start3 = ("hack-33025-touchpad_activity=<multiline>\n" +
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700181 "---------- START ----------\n" +
182 "begin-base64 644 touchpad_activity_log.tar\n")
Dennis Kempin91e96df2013-03-29 14:21:42 -0700183 if log_start1 in self.system:
184 log_start_index = self.system.index(log_start1) + len(log_start1)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700185 log_end_index = self.system.index("\"\"\"", log_start_index)
186 elif log_start2 in self.system:
187 log_start_index = self.system.index(log_start2) + len(log_start2)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700188 log_end_index = self.system.index("\"\"\"", log_start_index)
189 elif log_start3 in self.system:
190 log_start_index = self.system.index(log_start3) + len(log_start3)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700191 log_end_index = self.system.index("---------- END ----------",
192 log_start_index)
193 else:
194 print "cannot find touchpad_activity_log.tar in systems log file"
195 sys.exit(-1)
196
Dennis Kempin351024d2013-02-06 11:18:21 -0800197 activity_tar_enc = self.system[log_start_index:log_end_index]
198
199 # base64 decode
200 activity_tar_data = base64.b64decode(activity_tar_enc)
201
202 # untar
203 activity_tar_file = tarfile.open(fileobj=StringIO(activity_tar_data))
204
205 # find latest log files
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700206 logs = filter(lambda n: n.find("touchpad_activity") >= 0,
207 activity_tar_file.getnames())
208 if len(logs) == 1:
209 idx = 0
210 else:
211 while True:
212 print "Which log file would you like to use?"
213 for i, name in enumerate(logs):
214 print i, name
215 print ">"
216 selection = sys.stdin.readline()
217 try:
218 idx = int(selection)
219 if idx < 0 or idx >= len(logs):
220 print "Number out of range"
221 else:
222 break
223 except:
224 print "Not a number"
225
226 activity_name = logs[idx]
227
228 # find matching evdev file
229 evdev_name = logs[idx][0:logs[idx].index("touchpad_activity")]
230 evdev_name = evdev_name + "cmt_input_events"
231
232 for name in activity_tar_file.getnames():
233 if name.startswith(evdev_name):
234 evdev_name = name
235 break
236
237 activity_gz = activity_tar_file.extractfile(activity_name)
238 evdev_gz = activity_tar_file.extractfile(evdev_name)
Dennis Kempin351024d2013-02-06 11:18:21 -0800239
240 # decompress log files
241 self.activity = GzipFile(fileobj=activity_gz).read()
242 self.evdev = GzipFile(fileobj=evdev_gz).read()
243
244
245identity_message = """\
246In order to access devices in TPTool, you need to have password-less
247auth for chromebooks set up.
248Would you like tptool to run the following command for you?
249$ %s
250Yes/No? (Default: No)"""
251
252class DeviceLog(AbstractLog):
253 """
254 Downloads logs from a running chromebook via scp.
255 """
256 def __init__(self, ip, options):
257 self.id_path = os.path.expanduser("~/.ssh/identity")
258 if not os.path.exists(self.id_path):
259 self._SetupIdentity()
260
261 if options.new:
262 self._GenerateLogs(ip)
263
264 self.activity = self._Download(ip, "touchpad_activity_log.txt")
265 self.evdev = self._Download(ip, "cmt_input_events.dat")
266 self.system = ""
267
268 def _GenerateLogs(self, ip):
269 cmd = ("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
270 "-q root@%s /opt/google/touchpad/tpcontrol log") % ip
271 process = subprocess.Popen(cmd.split())
272 process.wait()
273
274 def _Download(self, ip, filename):
275 temp = tempfile.NamedTemporaryFile("r")
276 cmd = ("scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
277 "-q root@%s:/var/log/%s %s")
278 cmd = cmd % (ip, filename, temp.name)
279 process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
280 process.wait()
281 temp.seek(0)
282 return temp.read()
283
284 def _SetupIdentity(self):
285 source = os.path.expanduser("~/trunk/chromite/ssh_keys/testing_rsa")
286 command = "cp %s %s && chmod 600 %s" % (source, self.id_path, self.id_path)
287 print identity_message % command
288 response = sys.stdin.readline().strip()
289 if response in ("Yes", "yes", "Y", "y"):
290 print command
291 os.system(command)