blob: 63a3e774925fa6ddbb46367c8926cf57660bf0fa [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"):
36 return FeedbackLog(source, None)
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 """
49 A log file consists of the activity log, the evdev log and
50 a system log which can be saved to disk all together.
51 """
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)
58
59
60class FileLog(AbstractLog):
61 """
62 Loads log from file. Does not contain evdev or system logs.
63 """
64 def __init__(self, filename, options):
65 self.activity = open(filename).read()
66 self.evdev = ""
67 self.system = ""
Dennis Kempinfa6597c2013-02-20 14:05:19 -080068 if os.path.exists(filename + ".evdev"):
69 self.evdev = open(filename + ".evdev").read()
70 if os.path.exists(filename + ".system"):
71 self.system = open(filename + ".system").read()
Dennis Kempin351024d2013-02-06 11:18:21 -080072
73
74class FeedbackLog(AbstractLog):
75 """
Dennis Kempin91e96df2013-03-29 14:21:42 -070076 Downloads logs from a feedback id or file name
Dennis Kempin351024d2013-02-06 11:18:21 -080077 """
Dennis Kempin91e96df2013-03-29 14:21:42 -070078 def __init__(self, id_or_filename, options):
79 if id_or_filename.endswith(".zip") or id_or_filename.endswith(".bz2"):
80 self.report = open(id_or_filename).read()
81 else:
82 # setup cookies file and url opener then download the report archive
83 self.cookies = cookielib.MozillaCookieJar(cookies_file)
84 if os.path.exists(cookies_file):
85 self.cookies.load()
86 cookie_processor = urllib2.HTTPCookieProcessor(self.cookies)
87 opener = urllib2.build_opener(cookie_processor)
88 self._DownloadSystemLog(id_or_filename, opener)
Dennis Kempin351024d2013-02-06 11:18:21 -080089
Dennis Kempin91e96df2013-03-29 14:21:42 -070090 self._ExtractSystemLog()
Dennis Kempin351024d2013-02-06 11:18:21 -080091 self._ExtractLogFiles()
92
93 def _Login(self, opener):
94 print "Login to corp.google.com:"
95 username = getpass.getuser()
96 password = getpass.getpass()
97 sys.stdout.write("OTP: ")
98 otp = sys.stdin.readline().strip()
99
100 # execute login by posting userdata to login.corp.google.com
101 url = "https://login.corp.google.com/login?ssoformat=CORP_SSO"
102 data = "u=%s&pw=%s&otp=%s" % (username, password, otp)
103 result = opener.open(url, data).read()
104
105 # check if the result displays error
106 if result.find("error") >= 0:
107 print "Login failed"
108 exit(-1)
109
110 # login was successful. save cookies to file to be reused later.
111 self.cookies.save()
112
113 def _GetLatestFile(self, match, tar):
114 # find file name containing match with latest timestamp
115 names = filter(lambda n: n.find(match) >= 0, tar.getnames())
116 names.sort()
Dennis Kempin765ad942013-03-26 13:58:46 -0700117 if not names:
118 print "Cannot find files named %s in tar file" % match
119 print "Tar file contents:", tar.getnames()
120 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800121 return tar.extractfile(names[-1])
122
123 def _DownloadSystemLog(self, id, opener):
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700124 # First download the report.zip file
125 logfile = "report-%s-system_logs.zip" % id
Dennis Kempin351024d2013-02-06 11:18:21 -0800126 url = ("https://feedback.corp.googleusercontent.com/binarydata/"+
127 "%s?id=%s&logIndex=0") % (logfile, id)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700128 self.report = opener.open(url).read()
129 file("test.zip", "w").write(self.report)
Dennis Kempin351024d2013-02-06 11:18:21 -0800130
Dennis Kempin91e96df2013-03-29 14:21:42 -0700131 if self.report[0:2] != "BZ" and self.report[0:2] != "PK":
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700132 # not a zip/bzip file. Log in and try again.
Dennis Kempin351024d2013-02-06 11:18:21 -0800133 self._Login(opener)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700134 self.report = opener.open(url).read()
Dennis Kempin351024d2013-02-06 11:18:21 -0800135
Dennis Kempin91e96df2013-03-29 14:21:42 -0700136 def _ExtractSystemLog(self):
137 if self.report[0:2] == "BZ":
138 self.system = bz2.decompress(self.report)
139 elif self.report[0:2] == "PK":
140 io = StringIO(self.report)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700141 zip = zipfile.ZipFile(io, "r")
142 self.system = zip.read(zip.namelist()[0])
143 zip.extractall("./")
144 else:
145 print "Cannot download logs file"
146 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800147
148 def _ExtractLogFiles(self):
149 # find embedded and uuencoded activity.tar in system log
Dennis Kempin91e96df2013-03-29 14:21:42 -0700150 log_start1 = ("hack-33025-touchpad_activity=\"\"\"\n" +
Dennis Kempin351024d2013-02-06 11:18:21 -0800151 "begin-base64 644 touchpad_activity_log.tar\n")
Dennis Kempin91e96df2013-03-29 14:21:42 -0700152 log_start2 = ("touchpad_activity=\"\"\"\n" +
153 "begin-base64 644 touchpad_activity_log.tar\n")
154 log_start3 = ("hack-33025-touchpad_activity=<multiline>\n" +
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700155 "---------- START ----------\n" +
156 "begin-base64 644 touchpad_activity_log.tar\n")
Dennis Kempin91e96df2013-03-29 14:21:42 -0700157 if log_start1 in self.system:
158 log_start_index = self.system.index(log_start1) + len(log_start1)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700159 log_end_index = self.system.index("\"\"\"", log_start_index)
160 elif log_start2 in self.system:
161 log_start_index = self.system.index(log_start2) + len(log_start2)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700162 log_end_index = self.system.index("\"\"\"", log_start_index)
163 elif log_start3 in self.system:
164 log_start_index = self.system.index(log_start3) + len(log_start3)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700165 log_end_index = self.system.index("---------- END ----------",
166 log_start_index)
167 else:
168 print "cannot find touchpad_activity_log.tar in systems log file"
169 sys.exit(-1)
170
Dennis Kempin351024d2013-02-06 11:18:21 -0800171 activity_tar_enc = self.system[log_start_index:log_end_index]
172
173 # base64 decode
174 activity_tar_data = base64.b64decode(activity_tar_enc)
175
176 # untar
177 activity_tar_file = tarfile.open(fileobj=StringIO(activity_tar_data))
178
179 # find latest log files
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700180 logs = filter(lambda n: n.find("touchpad_activity") >= 0,
181 activity_tar_file.getnames())
182 if len(logs) == 1:
183 idx = 0
184 else:
185 while True:
186 print "Which log file would you like to use?"
187 for i, name in enumerate(logs):
188 print i, name
189 print ">"
190 selection = sys.stdin.readline()
191 try:
192 idx = int(selection)
193 if idx < 0 or idx >= len(logs):
194 print "Number out of range"
195 else:
196 break
197 except:
198 print "Not a number"
199
200 activity_name = logs[idx]
201
202 # find matching evdev file
203 evdev_name = logs[idx][0:logs[idx].index("touchpad_activity")]
204 evdev_name = evdev_name + "cmt_input_events"
205
206 for name in activity_tar_file.getnames():
207 if name.startswith(evdev_name):
208 evdev_name = name
209 break
210
211 activity_gz = activity_tar_file.extractfile(activity_name)
212 evdev_gz = activity_tar_file.extractfile(evdev_name)
Dennis Kempin351024d2013-02-06 11:18:21 -0800213
214 # decompress log files
215 self.activity = GzipFile(fileobj=activity_gz).read()
216 self.evdev = GzipFile(fileobj=evdev_gz).read()
217
218
219identity_message = """\
220In order to access devices in TPTool, you need to have password-less
221auth for chromebooks set up.
222Would you like tptool to run the following command for you?
223$ %s
224Yes/No? (Default: No)"""
225
226class DeviceLog(AbstractLog):
227 """
228 Downloads logs from a running chromebook via scp.
229 """
230 def __init__(self, ip, options):
231 self.id_path = os.path.expanduser("~/.ssh/identity")
232 if not os.path.exists(self.id_path):
233 self._SetupIdentity()
234
235 if options.new:
236 self._GenerateLogs(ip)
237
238 self.activity = self._Download(ip, "touchpad_activity_log.txt")
239 self.evdev = self._Download(ip, "cmt_input_events.dat")
240 self.system = ""
241
242 def _GenerateLogs(self, ip):
243 cmd = ("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
244 "-q root@%s /opt/google/touchpad/tpcontrol log") % ip
245 process = subprocess.Popen(cmd.split())
246 process.wait()
247
248 def _Download(self, ip, filename):
249 temp = tempfile.NamedTemporaryFile("r")
250 cmd = ("scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
251 "-q root@%s:/var/log/%s %s")
252 cmd = cmd % (ip, filename, temp.name)
253 process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
254 process.wait()
255 temp.seek(0)
256 return temp.read()
257
258 def _SetupIdentity(self):
259 source = os.path.expanduser("~/trunk/chromite/ssh_keys/testing_rsa")
260 command = "cp %s %s && chmod 600 %s" % (source, self.id_path, self.id_path)
261 print identity_message % command
262 response = sys.stdin.readline().strip()
263 if response in ("Yes", "yes", "Y", "y"):
264 print command
265 os.system(command)