blob: 52c5a7c305bec6d421f5020219d84d0abf9448d6 [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, Wendc6e0fb2013-05-09 13:11:35 -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, Wendc6e0fb2013-05-09 13:11:35 -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, Wendc6e0fb2013-05-09 13:11:35 -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, Wendc6e0fb2013-05-09 13:11:35 -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, Wendc6e0fb2013-05-09 13:11:35 -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, Wendc6e0fb2013-05-09 13:11:35 -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, Wendc6e0fb2013-05-09 13:11:35 -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:
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, Wendc6e0fb2013-05-09 13:11:35 -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, Wendc6e0fb2013-05-09 13:11:35 -0400154 def _DownloadScreenshot(self, id, opener):
155 url = "https://feedback.corp.googleusercontent.com/screenshot?id=%s" % id
156 try:
157 self.image = opener.open(url).read()
158 except urllib2.URLError:
159 print "No screenshots available - %s" % url
160
Dennis Kempin91e96df2013-03-29 14:21:42 -0700161 def _ExtractSystemLog(self):
162 if self.report[0:2] == "BZ":
163 self.system = bz2.decompress(self.report)
164 elif self.report[0:2] == "PK":
165 io = StringIO(self.report)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700166 zip = zipfile.ZipFile(io, "r")
167 self.system = zip.read(zip.namelist()[0])
168 zip.extractall("./")
169 else:
170 print "Cannot download logs file"
171 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800172
173 def _ExtractLogFiles(self):
174 # find embedded and uuencoded activity.tar in system log
Dennis Kempin91e96df2013-03-29 14:21:42 -0700175 log_start1 = ("hack-33025-touchpad_activity=\"\"\"\n" +
Dennis Kempin351024d2013-02-06 11:18:21 -0800176 "begin-base64 644 touchpad_activity_log.tar\n")
Dennis Kempin91e96df2013-03-29 14:21:42 -0700177 log_start2 = ("touchpad_activity=\"\"\"\n" +
178 "begin-base64 644 touchpad_activity_log.tar\n")
179 log_start3 = ("hack-33025-touchpad_activity=<multiline>\n" +
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700180 "---------- START ----------\n" +
181 "begin-base64 644 touchpad_activity_log.tar\n")
Dennis Kempin91e96df2013-03-29 14:21:42 -0700182 if log_start1 in self.system:
183 log_start_index = self.system.index(log_start1) + len(log_start1)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700184 log_end_index = self.system.index("\"\"\"", log_start_index)
185 elif log_start2 in self.system:
186 log_start_index = self.system.index(log_start2) + len(log_start2)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700187 log_end_index = self.system.index("\"\"\"", log_start_index)
188 elif log_start3 in self.system:
189 log_start_index = self.system.index(log_start3) + len(log_start3)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700190 log_end_index = self.system.index("---------- END ----------",
191 log_start_index)
192 else:
193 print "cannot find touchpad_activity_log.tar in systems log file"
194 sys.exit(-1)
195
Dennis Kempin351024d2013-02-06 11:18:21 -0800196 activity_tar_enc = self.system[log_start_index:log_end_index]
197
198 # base64 decode
199 activity_tar_data = base64.b64decode(activity_tar_enc)
200
201 # untar
202 activity_tar_file = tarfile.open(fileobj=StringIO(activity_tar_data))
203
204 # find latest log files
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700205 logs = filter(lambda n: n.find("touchpad_activity") >= 0,
206 activity_tar_file.getnames())
207 if len(logs) == 1:
208 idx = 0
209 else:
210 while True:
211 print "Which log file would you like to use?"
212 for i, name in enumerate(logs):
213 print i, name
214 print ">"
215 selection = sys.stdin.readline()
216 try:
217 idx = int(selection)
218 if idx < 0 or idx >= len(logs):
219 print "Number out of range"
220 else:
221 break
222 except:
223 print "Not a number"
224
225 activity_name = logs[idx]
226
227 # find matching evdev file
228 evdev_name = logs[idx][0:logs[idx].index("touchpad_activity")]
229 evdev_name = evdev_name + "cmt_input_events"
230
231 for name in activity_tar_file.getnames():
232 if name.startswith(evdev_name):
233 evdev_name = name
234 break
235
236 activity_gz = activity_tar_file.extractfile(activity_name)
237 evdev_gz = activity_tar_file.extractfile(evdev_name)
Dennis Kempin351024d2013-02-06 11:18:21 -0800238
239 # decompress log files
240 self.activity = GzipFile(fileobj=activity_gz).read()
241 self.evdev = GzipFile(fileobj=evdev_gz).read()
242
243
244identity_message = """\
245In order to access devices in TPTool, you need to have password-less
246auth for chromebooks set up.
247Would you like tptool to run the following command for you?
248$ %s
249Yes/No? (Default: No)"""
250
251class DeviceLog(AbstractLog):
252 """
253 Downloads logs from a running chromebook via scp.
254 """
255 def __init__(self, ip, options):
256 self.id_path = os.path.expanduser("~/.ssh/identity")
257 if not os.path.exists(self.id_path):
258 self._SetupIdentity()
259
260 if options.new:
261 self._GenerateLogs(ip)
262
263 self.activity = self._Download(ip, "touchpad_activity_log.txt")
264 self.evdev = self._Download(ip, "cmt_input_events.dat")
265 self.system = ""
266
267 def _GenerateLogs(self, ip):
268 cmd = ("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
269 "-q root@%s /opt/google/touchpad/tpcontrol log") % ip
270 process = subprocess.Popen(cmd.split())
271 process.wait()
272
273 def _Download(self, ip, filename):
274 temp = tempfile.NamedTemporaryFile("r")
275 cmd = ("scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
276 "-q root@%s:/var/log/%s %s")
277 cmd = cmd % (ip, filename, temp.name)
278 process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
279 process.wait()
280 temp.seek(0)
281 return temp.read()
282
283 def _SetupIdentity(self):
284 source = os.path.expanduser("~/trunk/chromite/ssh_keys/testing_rsa")
285 command = "cp %s %s && chmod 600 %s" % (source, self.id_path, self.id_path)
286 print identity_message % command
287 response = sys.stdin.readline().strip()
288 if response in ("Yes", "yes", "Y", "y"):
289 print command
290 os.system(command)