blob: 52bff60c4c561c5463ebe24cd52255c2189151a1 [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
19
20
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):
35 return FileLog(source, options)
36 else:
37 match = re.search("Report/([0-9]+)", source)
38 if match:
39 return FeedbackLog(match.group(1), options)
40 else:
41 # todo add regex and error message
42 return DeviceLog(source, options)
43
44
45class AbstractLog(object):
46 """
47 A log file consists of the activity log, the evdev log and
48 a system log which can be saved to disk all together.
49 """
50 def SaveAs(self, filename):
51 open(filename, "w").write(self.activity)
52 if self.evdev:
53 open(filename + ".evdev", "w").write(self.evdev)
54 if self.system:
55 open(filename + ".system", "w").write(self.system)
56
57
58class FileLog(AbstractLog):
59 """
60 Loads log from file. Does not contain evdev or system logs.
61 """
62 def __init__(self, filename, options):
63 self.activity = open(filename).read()
64 self.evdev = ""
65 self.system = ""
Dennis Kempinfa6597c2013-02-20 14:05:19 -080066 if os.path.exists(filename + ".evdev"):
67 self.evdev = open(filename + ".evdev").read()
68 if os.path.exists(filename + ".system"):
69 self.system = open(filename + ".system").read()
Dennis Kempin351024d2013-02-06 11:18:21 -080070
71
72class FeedbackLog(AbstractLog):
73 """
74 Downloads logs from a feedback id.
75 """
76 def __init__(self, id, options):
77 # setup cookies file and url opener
78 self.cookies = cookielib.MozillaCookieJar(cookies_file)
79 if os.path.exists(cookies_file):
80 self.cookies.load()
81 cookie_processor = urllib2.HTTPCookieProcessor(self.cookies)
82 opener = urllib2.build_opener(cookie_processor)
83
84 self._DownloadSystemLog(id, opener)
85 self._ExtractLogFiles()
86
87 def _Login(self, opener):
88 print "Login to corp.google.com:"
89 username = getpass.getuser()
90 password = getpass.getpass()
91 sys.stdout.write("OTP: ")
92 otp = sys.stdin.readline().strip()
93
94 # execute login by posting userdata to login.corp.google.com
95 url = "https://login.corp.google.com/login?ssoformat=CORP_SSO"
96 data = "u=%s&pw=%s&otp=%s" % (username, password, otp)
97 result = opener.open(url, data).read()
98
99 # check if the result displays error
100 if result.find("error") >= 0:
101 print "Login failed"
102 exit(-1)
103
104 # login was successful. save cookies to file to be reused later.
105 self.cookies.save()
106
107 def _GetLatestFile(self, match, tar):
108 # find file name containing match with latest timestamp
109 names = filter(lambda n: n.find(match) >= 0, tar.getnames())
110 names.sort()
Dennis Kempin765ad942013-03-26 13:58:46 -0700111 if not names:
112 print "Cannot find files named %s in tar file" % match
113 print "Tar file contents:", tar.getnames()
114 sys.exit(-1)
Dennis Kempin351024d2013-02-06 11:18:21 -0800115 return tar.extractfile(names[-1])
116
117 def _DownloadSystemLog(self, id, opener):
118 # First download the report.bz2 file
119 logfile = "report-%s-system_logs.bz2" % id
120 url = ("https://feedback.corp.googleusercontent.com/binarydata/"+
121 "%s?id=%s&logIndex=0") % (logfile, id)
122 report_bz2 = opener.open(url).read()
123
124 if report_bz2[0:2] != "BZ":
125 # not a bzip file. Log in and try again.
126 self._Login(opener)
127 report_bz2 = opener.open(url).read()
128
129 # unpack bzip file
130 self.system = bz2.decompress(report_bz2)
131
132 def _ExtractLogFiles(self):
133 # find embedded and uuencoded activity.tar in system log
134 log_start = ("hack-33025-touchpad_activity=\"\"\"\n" +
135 "begin-base64 644 touchpad_activity_log.tar\n")
136 log_start_index = self.system.index(log_start) + len(log_start)
137 log_end_index = self.system.index("\"\"\"", log_start_index)
138 activity_tar_enc = self.system[log_start_index:log_end_index]
139
140 # base64 decode
141 activity_tar_data = base64.b64decode(activity_tar_enc)
142
143 # untar
144 activity_tar_file = tarfile.open(fileobj=StringIO(activity_tar_data))
145
146 # find latest log files
147 activity_gz = self._GetLatestFile("touchpad_activity", activity_tar_file)
148 evdev_gz = self._GetLatestFile("cmt_input_events", activity_tar_file)
149
150 # decompress log files
151 self.activity = GzipFile(fileobj=activity_gz).read()
152 self.evdev = GzipFile(fileobj=evdev_gz).read()
153
154
155identity_message = """\
156In order to access devices in TPTool, you need to have password-less
157auth for chromebooks set up.
158Would you like tptool to run the following command for you?
159$ %s
160Yes/No? (Default: No)"""
161
162class DeviceLog(AbstractLog):
163 """
164 Downloads logs from a running chromebook via scp.
165 """
166 def __init__(self, ip, options):
167 self.id_path = os.path.expanduser("~/.ssh/identity")
168 if not os.path.exists(self.id_path):
169 self._SetupIdentity()
170
171 if options.new:
172 self._GenerateLogs(ip)
173
174 self.activity = self._Download(ip, "touchpad_activity_log.txt")
175 self.evdev = self._Download(ip, "cmt_input_events.dat")
176 self.system = ""
177
178 def _GenerateLogs(self, ip):
179 cmd = ("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
180 "-q root@%s /opt/google/touchpad/tpcontrol log") % ip
181 process = subprocess.Popen(cmd.split())
182 process.wait()
183
184 def _Download(self, ip, filename):
185 temp = tempfile.NamedTemporaryFile("r")
186 cmd = ("scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
187 "-q root@%s:/var/log/%s %s")
188 cmd = cmd % (ip, filename, temp.name)
189 process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
190 process.wait()
191 temp.seek(0)
192 return temp.read()
193
194 def _SetupIdentity(self):
195 source = os.path.expanduser("~/trunk/chromite/ssh_keys/testing_rsa")
196 command = "cp %s %s && chmod 600 %s" % (source, self.id_path, self.id_path)
197 print identity_message % command
198 response = sys.stdin.readline().strip()
199 if response in ("Yes", "yes", "Y", "y"):
200 print command
201 os.system(command)