blob: bde4fee47cfa7f327a3dfe6dbcc27b1cd967b593 [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.
25cookies_file = os.path.join(script_dir, "mtview.cookies")
26
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 = ""
66
67
68class FeedbackLog(AbstractLog):
69 """
70 Downloads logs from a feedback id.
71 """
72 def __init__(self, id, options):
73 # setup cookies file and url opener
74 self.cookies = cookielib.MozillaCookieJar(cookies_file)
75 if os.path.exists(cookies_file):
76 self.cookies.load()
77 cookie_processor = urllib2.HTTPCookieProcessor(self.cookies)
78 opener = urllib2.build_opener(cookie_processor)
79
80 self._DownloadSystemLog(id, opener)
81 self._ExtractLogFiles()
82
83 def _Login(self, opener):
84 print "Login to corp.google.com:"
85 username = getpass.getuser()
86 password = getpass.getpass()
87 sys.stdout.write("OTP: ")
88 otp = sys.stdin.readline().strip()
89
90 # execute login by posting userdata to login.corp.google.com
91 url = "https://login.corp.google.com/login?ssoformat=CORP_SSO"
92 data = "u=%s&pw=%s&otp=%s" % (username, password, otp)
93 result = opener.open(url, data).read()
94
95 # check if the result displays error
96 if result.find("error") >= 0:
97 print "Login failed"
98 exit(-1)
99
100 # login was successful. save cookies to file to be reused later.
101 self.cookies.save()
102
103 def _GetLatestFile(self, match, tar):
104 # find file name containing match with latest timestamp
105 names = filter(lambda n: n.find(match) >= 0, tar.getnames())
106 names.sort()
107 return tar.extractfile(names[-1])
108
109 def _DownloadSystemLog(self, id, opener):
110 # First download the report.bz2 file
111 logfile = "report-%s-system_logs.bz2" % id
112 url = ("https://feedback.corp.googleusercontent.com/binarydata/"+
113 "%s?id=%s&logIndex=0") % (logfile, id)
114 report_bz2 = opener.open(url).read()
115
116 if report_bz2[0:2] != "BZ":
117 # not a bzip file. Log in and try again.
118 self._Login(opener)
119 report_bz2 = opener.open(url).read()
120
121 # unpack bzip file
122 self.system = bz2.decompress(report_bz2)
123
124 def _ExtractLogFiles(self):
125 # find embedded and uuencoded activity.tar in system log
126 log_start = ("hack-33025-touchpad_activity=\"\"\"\n" +
127 "begin-base64 644 touchpad_activity_log.tar\n")
128 log_start_index = self.system.index(log_start) + len(log_start)
129 log_end_index = self.system.index("\"\"\"", log_start_index)
130 activity_tar_enc = self.system[log_start_index:log_end_index]
131
132 # base64 decode
133 activity_tar_data = base64.b64decode(activity_tar_enc)
134
135 # untar
136 activity_tar_file = tarfile.open(fileobj=StringIO(activity_tar_data))
137
138 # find latest log files
139 activity_gz = self._GetLatestFile("touchpad_activity", activity_tar_file)
140 evdev_gz = self._GetLatestFile("cmt_input_events", activity_tar_file)
141
142 # decompress log files
143 self.activity = GzipFile(fileobj=activity_gz).read()
144 self.evdev = GzipFile(fileobj=evdev_gz).read()
145
146
147identity_message = """\
148In order to access devices in TPTool, you need to have password-less
149auth for chromebooks set up.
150Would you like tptool to run the following command for you?
151$ %s
152Yes/No? (Default: No)"""
153
154class DeviceLog(AbstractLog):
155 """
156 Downloads logs from a running chromebook via scp.
157 """
158 def __init__(self, ip, options):
159 self.id_path = os.path.expanduser("~/.ssh/identity")
160 if not os.path.exists(self.id_path):
161 self._SetupIdentity()
162
163 if options.new:
164 self._GenerateLogs(ip)
165
166 self.activity = self._Download(ip, "touchpad_activity_log.txt")
167 self.evdev = self._Download(ip, "cmt_input_events.dat")
168 self.system = ""
169
170 def _GenerateLogs(self, ip):
171 cmd = ("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
172 "-q root@%s /opt/google/touchpad/tpcontrol log") % ip
173 process = subprocess.Popen(cmd.split())
174 process.wait()
175
176 def _Download(self, ip, filename):
177 temp = tempfile.NamedTemporaryFile("r")
178 cmd = ("scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
179 "-q root@%s:/var/log/%s %s")
180 cmd = cmd % (ip, filename, temp.name)
181 process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
182 process.wait()
183 temp.seek(0)
184 return temp.read()
185
186 def _SetupIdentity(self):
187 source = os.path.expanduser("~/trunk/chromite/ssh_keys/testing_rsa")
188 command = "cp %s %s && chmod 600 %s" % (source, self.id_path, self.id_path)
189 print identity_message % command
190 response = sys.stdin.readline().strip()
191 if response in ("Yes", "yes", "Y", "y"):
192 print command
193 os.system(command)