blob: 1dec52d1ab0fa2a3d3f32592f03e9551ddb2121f [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()
111 return tar.extractfile(names[-1])
112
113 def _DownloadSystemLog(self, id, opener):
114 # First download the report.bz2 file
115 logfile = "report-%s-system_logs.bz2" % id
116 url = ("https://feedback.corp.googleusercontent.com/binarydata/"+
117 "%s?id=%s&logIndex=0") % (logfile, id)
118 report_bz2 = opener.open(url).read()
119
120 if report_bz2[0:2] != "BZ":
121 # not a bzip file. Log in and try again.
122 self._Login(opener)
123 report_bz2 = opener.open(url).read()
124
125 # unpack bzip file
126 self.system = bz2.decompress(report_bz2)
127
128 def _ExtractLogFiles(self):
129 # find embedded and uuencoded activity.tar in system log
130 log_start = ("hack-33025-touchpad_activity=\"\"\"\n" +
131 "begin-base64 644 touchpad_activity_log.tar\n")
132 log_start_index = self.system.index(log_start) + len(log_start)
133 log_end_index = self.system.index("\"\"\"", log_start_index)
134 activity_tar_enc = self.system[log_start_index:log_end_index]
135
136 # base64 decode
137 activity_tar_data = base64.b64decode(activity_tar_enc)
138
139 # untar
140 activity_tar_file = tarfile.open(fileobj=StringIO(activity_tar_data))
141
142 # find latest log files
143 activity_gz = self._GetLatestFile("touchpad_activity", activity_tar_file)
144 evdev_gz = self._GetLatestFile("cmt_input_events", activity_tar_file)
145
146 # decompress log files
147 self.activity = GzipFile(fileobj=activity_gz).read()
148 self.evdev = GzipFile(fileobj=evdev_gz).read()
149
150
151identity_message = """\
152In order to access devices in TPTool, you need to have password-less
153auth for chromebooks set up.
154Would you like tptool to run the following command for you?
155$ %s
156Yes/No? (Default: No)"""
157
158class DeviceLog(AbstractLog):
159 """
160 Downloads logs from a running chromebook via scp.
161 """
162 def __init__(self, ip, options):
163 self.id_path = os.path.expanduser("~/.ssh/identity")
164 if not os.path.exists(self.id_path):
165 self._SetupIdentity()
166
167 if options.new:
168 self._GenerateLogs(ip)
169
170 self.activity = self._Download(ip, "touchpad_activity_log.txt")
171 self.evdev = self._Download(ip, "cmt_input_events.dat")
172 self.system = ""
173
174 def _GenerateLogs(self, ip):
175 cmd = ("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
176 "-q root@%s /opt/google/touchpad/tpcontrol log") % ip
177 process = subprocess.Popen(cmd.split())
178 process.wait()
179
180 def _Download(self, ip, filename):
181 temp = tempfile.NamedTemporaryFile("r")
182 cmd = ("scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no " +
183 "-q root@%s:/var/log/%s %s")
184 cmd = cmd % (ip, filename, temp.name)
185 process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
186 process.wait()
187 temp.seek(0)
188 return temp.read()
189
190 def _SetupIdentity(self):
191 source = os.path.expanduser("~/trunk/chromite/ssh_keys/testing_rsa")
192 command = "cp %s %s && chmod 600 %s" % (source, self.id_path, self.id_path)
193 print identity_message % command
194 response = sys.stdin.readline().strip()
195 if response in ("Yes", "yes", "Y", "y"):
196 print command
197 os.system(command)