blob: bccedbd82ab57ec97f2a42f4cbcb59f12368658b [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"):
Andrew de los Reyesa6dcd0e2013-05-10 12:38:43 -070036 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 """
Andrew de los Reyesa6dcd0e2013-05-10 12:38:43 -070049 A log file consists of the activity log, the evdev log and
50 a system log 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)
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 Kempin7b783272013-04-01 15:06:35 -070068 if options.evdev:
69 self.evdev = open(options.evdev).read()
70 elif os.path.exists(filename + ".evdev"):
Dennis Kempinfa6597c2013-02-20 14:05:19 -080071 self.evdev = open(filename + ".evdev").read()
72 if os.path.exists(filename + ".system"):
73 self.system = open(filename + ".system").read()
Dennis Kempin351024d2013-02-06 11:18:21 -080074
75
76class FeedbackLog(AbstractLog):
77 """
Andrew de los Reyesa6dcd0e2013-05-10 12:38:43 -070078 Downloads logs from a feedback id or file name
Dennis Kempin351024d2013-02-06 11:18:21 -080079 """
Dennis Kempin91e96df2013-03-29 14:21:42 -070080 def __init__(self, id_or_filename, options):
81 if id_or_filename.endswith(".zip") or id_or_filename.endswith(".bz2"):
82 self.report = open(id_or_filename).read()
83 else:
84 # setup cookies file and url opener then download the report archive
85 self.cookies = cookielib.MozillaCookieJar(cookies_file)
86 if os.path.exists(cookies_file):
87 self.cookies.load()
88 cookie_processor = urllib2.HTTPCookieProcessor(self.cookies)
89 opener = urllib2.build_opener(cookie_processor)
90 self._DownloadSystemLog(id_or_filename, opener)
Dennis Kempin351024d2013-02-06 11:18:21 -080091
Dennis Kempin91e96df2013-03-29 14:21:42 -070092 self._ExtractSystemLog()
Dennis Kempin351024d2013-02-06 11:18:21 -080093 self._ExtractLogFiles()
94
95 def _Login(self, opener):
96 print "Login to corp.google.com:"
97 username = getpass.getuser()
98 password = getpass.getpass()
99 sys.stdout.write("OTP: ")
100 otp = sys.stdin.readline().strip()
101
102 # execute login by posting userdata to login.corp.google.com
103 url = "https://login.corp.google.com/login?ssoformat=CORP_SSO"
104 data = "u=%s&pw=%s&otp=%s" % (username, password, otp)
105 result = opener.open(url, data).read()
106
107 # check if the result displays error
108 if result.find("error") >= 0:
109 print "Login failed"
110 exit(-1)
111
112 # login was successful. save cookies to file to be reused later.
113 self.cookies.save()
114
115 def _GetLatestFile(self, match, tar):
116 # find file name containing match with latest timestamp
117 names = filter(lambda n: n.find(match) >= 0, tar.getnames())
118 names.sort()
Dennis Kempin765ad942013-03-26 13:58:46 -0700119 if not names:
120 print "Cannot find files named %s in tar file" % match
121 print "Tar file contents:", tar.getnames()
122 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800123 return tar.extractfile(names[-1])
124
125 def _DownloadSystemLog(self, id, opener):
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700126 # First download the report.zip file
127 logfile = "report-%s-system_logs.zip" % id
Andrew de los Reyesa6dcd0e2013-05-10 12:38:43 -0700128 url = ("https://feedback.corp.googleusercontent.com/binarydata/"+
Dennis Kempin351024d2013-02-06 11:18:21 -0800129 "%s?id=%s&logIndex=0") % (logfile, id)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700130 self.report = opener.open(url).read()
131 file("test.zip", "w").write(self.report)
Dennis Kempin351024d2013-02-06 11:18:21 -0800132
Dennis Kempin91e96df2013-03-29 14:21:42 -0700133 if self.report[0:2] != "BZ" and self.report[0:2] != "PK":
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700134 # not a zip/bzip file. Log in and try again.
Dennis Kempin351024d2013-02-06 11:18:21 -0800135 self._Login(opener)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700136 self.report = opener.open(url).read()
Dennis Kempin351024d2013-02-06 11:18:21 -0800137
Dennis Kempin91e96df2013-03-29 14:21:42 -0700138 def _ExtractSystemLog(self):
139 if self.report[0:2] == "BZ":
140 self.system = bz2.decompress(self.report)
141 elif self.report[0:2] == "PK":
142 io = StringIO(self.report)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700143 zip = zipfile.ZipFile(io, "r")
144 self.system = zip.read(zip.namelist()[0])
145 zip.extractall("./")
146 else:
147 print "Cannot download logs file"
148 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800149
150 def _ExtractLogFiles(self):
151 # find embedded and uuencoded activity.tar in system log
Dennis Kempin91e96df2013-03-29 14:21:42 -0700152 log_start1 = ("hack-33025-touchpad_activity=\"\"\"\n" +
Dennis Kempin351024d2013-02-06 11:18:21 -0800153 "begin-base64 644 touchpad_activity_log.tar\n")
Dennis Kempin91e96df2013-03-29 14:21:42 -0700154 log_start2 = ("touchpad_activity=\"\"\"\n" +
155 "begin-base64 644 touchpad_activity_log.tar\n")
156 log_start3 = ("hack-33025-touchpad_activity=<multiline>\n" +
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700157 "---------- START ----------\n" +
158 "begin-base64 644 touchpad_activity_log.tar\n")
Dennis Kempin91e96df2013-03-29 14:21:42 -0700159 if log_start1 in self.system:
160 log_start_index = self.system.index(log_start1) + len(log_start1)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700161 log_end_index = self.system.index("\"\"\"", log_start_index)
162 elif log_start2 in self.system:
163 log_start_index = self.system.index(log_start2) + len(log_start2)
Dennis Kempin91e96df2013-03-29 14:21:42 -0700164 log_end_index = self.system.index("\"\"\"", log_start_index)
165 elif log_start3 in self.system:
166 log_start_index = self.system.index(log_start3) + len(log_start3)
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700167 log_end_index = self.system.index("---------- END ----------",
168 log_start_index)
169 else:
170 print "cannot find touchpad_activity_log.tar in systems log file"
171 sys.exit(-1)
172
Dennis Kempin351024d2013-02-06 11:18:21 -0800173 activity_tar_enc = self.system[log_start_index:log_end_index]
174
175 # base64 decode
176 activity_tar_data = base64.b64decode(activity_tar_enc)
177
178 # untar
179 activity_tar_file = tarfile.open(fileobj=StringIO(activity_tar_data))
180
181 # find latest log files
Dennis Kempinecd9a0c2013-03-27 16:03:46 -0700182 logs = filter(lambda n: n.find("touchpad_activity") >= 0,
183 activity_tar_file.getnames())
184 if len(logs) == 1:
185 idx = 0
186 else:
187 while True:
188 print "Which log file would you like to use?"
189 for i, name in enumerate(logs):
190 print i, name
191 print ">"
192 selection = sys.stdin.readline()
193 try:
194 idx = int(selection)
195 if idx < 0 or idx >= len(logs):
196 print "Number out of range"
197 else:
198 break
199 except:
200 print "Not a number"
201
202 activity_name = logs[idx]
203
204 # find matching evdev file
205 evdev_name = logs[idx][0:logs[idx].index("touchpad_activity")]
206 evdev_name = evdev_name + "cmt_input_events"
207
208 for name in activity_tar_file.getnames():
209 if name.startswith(evdev_name):
210 evdev_name = name
211 break
212
213 activity_gz = activity_tar_file.extractfile(activity_name)
214 evdev_gz = activity_tar_file.extractfile(evdev_name)
Dennis Kempin351024d2013-02-06 11:18:21 -0800215
216 # decompress log files
217 self.activity = GzipFile(fileobj=activity_gz).read()
218 self.evdev = GzipFile(fileobj=evdev_gz).read()
219
220
221identity_message = """\
222In order to access devices in TPTool, you need to have password-less
223auth for chromebooks set up.
224Would you like tptool to run the following command for you?
225$ %s
226Yes/No? (Default: No)"""
227
228class DeviceLog(AbstractLog):
229 """
230 Downloads logs from a running chromebook via scp.
231 """
232 def __init__(self, ip, options):
233 self.id_path = os.path.expanduser("~/.ssh/identity")
234 if not os.path.exists(self.id_path):
235 self._SetupIdentity()
236
237 if options.new:
238 self._GenerateLogs(ip)
239
240 self.activity = self._Download(ip, "touchpad_activity_log.txt")
241 self.evdev = self._Download(ip, "cmt_input_events.dat")
242 self.system = ""
243
244 def _GenerateLogs(self, ip):
245 cmd = ("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
246 "-q root@%s /opt/google/touchpad/tpcontrol log") % ip
247 process = subprocess.Popen(cmd.split())
248 process.wait()
249
250 def _Download(self, ip, filename):
251 temp = tempfile.NamedTemporaryFile("r")
252 cmd = ("scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
253 "-q root@%s:/var/log/%s %s")
254 cmd = cmd % (ip, filename, temp.name)
255 process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
256 process.wait()
257 temp.seek(0)
258 return temp.read()
259
260 def _SetupIdentity(self):
261 source = os.path.expanduser("~/trunk/chromite/ssh_keys/testing_rsa")
262 command = "cp %s %s && chmod 600 %s" % (source, self.id_path, self.id_path)
263 print identity_message % command
264 response = sys.stdin.readline().strip()
265 if response in ("Yes", "yes", "Y", "y"):
266 print command
267 os.system(command)