|
0 |
#!/usr/bin/env python
|
|
1 |
#
|
|
2 |
# Copyright (C) 2007, Pascal Scheffers <pascal@scheffers.net>
|
|
3 |
#
|
|
4 |
# Permission is hereby granted, free of charge, to any person
|
|
5 |
# obtaining a copy of this software and associated documentation
|
|
6 |
# files (the "Software"), to deal in the Software without
|
|
7 |
# restriction, including without limitation the rights to use,
|
|
8 |
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9 |
# copies of the Software, and to permit persons to whom the
|
|
10 |
# Software is furnished to do so, subject to the following
|
|
11 |
# conditions:
|
|
12 |
#
|
|
13 |
# The above copyright notice and this permission notice shall be
|
|
14 |
# included in all copies or substantial portions of the Software.
|
|
15 |
#
|
|
16 |
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17 |
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
18 |
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19 |
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
20 |
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
21 |
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
22 |
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
23 |
# OTHER DEALINGS IN THE SOFTWARE.
|
|
24 |
#
|
|
25 |
# log-collect for OLPC
|
|
26 |
#
|
|
27 |
# Compile a report containing:
|
|
28 |
# * Basic system information:
|
|
29 |
# ** Serial number
|
|
30 |
# ** Battery type
|
|
31 |
# ** Build number
|
|
32 |
# ** Uptime
|
|
33 |
# ** disk free space
|
|
34 |
# ** ...
|
|
35 |
# * Installed packages list
|
|
36 |
# * All relevant log files (all of them, at first)
|
|
37 |
#
|
|
38 |
# The report is output as a tarfile
|
|
39 |
#
|
|
40 |
# This file has two modes:
|
|
41 |
# 1. It is a stand-alone python script, when invoked as 'log-collect'
|
|
42 |
# 2. It is a python module.
|
|
43 |
|
|
44 |
import os
|
|
45 |
import zipfile
|
|
46 |
import glob
|
|
47 |
import sys
|
|
48 |
import time
|
|
49 |
|
|
50 |
# The next couple are used by LogSend
|
|
51 |
import httplib
|
|
52 |
import mimetypes
|
|
53 |
import urlparse
|
|
54 |
|
|
55 |
class MachineProperties:
|
|
56 |
"""Various machine properties in easy to access chunks.
|
|
57 |
"""
|
|
58 |
|
|
59 |
def __read_file(self, filename):
|
|
60 |
"""Read the entire contents of a file and return it as a string"""
|
|
61 |
|
|
62 |
data = ''
|
|
63 |
|
|
64 |
f = open(filename)
|
|
65 |
try:
|
|
66 |
data = f.read()
|
|
67 |
finally:
|
|
68 |
f.close()
|
|
69 |
|
|
70 |
return data
|
|
71 |
|
|
72 |
def olpc_build(self):
|
|
73 |
"""Buildnumber, from /etc/issue"""
|
|
74 |
# Is there a better place to get the build number?
|
|
75 |
if not os.path.exists('/etc/issue'):
|
|
76 |
return '#/etc/issue not found'
|
|
77 |
|
|
78 |
# Needed, because we want to default to the first non blank line:
|
|
79 |
first_line = ''
|
|
80 |
|
|
81 |
for line in self.__read_file('/etc/issue').splitlines():
|
|
82 |
if line.lower().find('olpc build') > -1:
|
|
83 |
return line
|
|
84 |
if first_line == '':
|
|
85 |
first_line=line
|
|
86 |
|
|
87 |
return first_line
|
|
88 |
|
|
89 |
def uptime(self):
|
|
90 |
for line in self.__read_file('/proc/uptime').splitlines():
|
|
91 |
if line != '':
|
|
92 |
return line
|
|
93 |
return ''
|
|
94 |
|
|
95 |
def loadavg(self):
|
|
96 |
for line in self.__read_file('/proc/loadavg').splitlines():
|
|
97 |
if line != '':
|
|
98 |
return line
|
|
99 |
return ''
|
|
100 |
|
|
101 |
def kernel_version(self):
|
|
102 |
for line in self.__read_file('/proc/version').splitlines():
|
|
103 |
if line != '':
|
|
104 |
return line
|
|
105 |
return ''
|
|
106 |
|
|
107 |
def memfree(self):
|
|
108 |
line = ''
|
|
109 |
|
|
110 |
for line in self.__read_file('/proc/meminfo').splitlines():
|
|
111 |
if line.find('MemFree:') > -1:
|
|
112 |
return line[8:].strip()
|
|
113 |
|
|
114 |
def _mfg_data(self, item):
|
|
115 |
"""Return mfg data item from /ofw/mfg-data/"""
|
|
116 |
|
|
117 |
if not os.path.exists('/ofw/mfg-data/'+item):
|
|
118 |
return ''
|
|
119 |
|
|
120 |
v = self.__read_file('/ofw/mfg-data/'+item)
|
|
121 |
# Remove trailing 0 character, if any:
|
|
122 |
if v != '' and ord(v[len(v)-1]) == 0:
|
|
123 |
v = v[:len(v)-1]
|
|
124 |
|
|
125 |
return v
|
|
126 |
|
|
127 |
def laptop_serial_number(self):
|
|
128 |
return self._mfg_data('SN')
|
|
129 |
|
|
130 |
def laptop_motherboard_number(self):
|
|
131 |
return self._mfg_data('B#')
|
|
132 |
|
|
133 |
def laptop_board_revision(self):
|
|
134 |
s = self._mfg_data('SG')[0:1]
|
|
135 |
if s == '':
|
|
136 |
return ''
|
|
137 |
|
|
138 |
return '%02X' % ord(self._mfg_data('SG')[0:1])
|
|
139 |
|
|
140 |
|
|
141 |
def laptop_uuid(self):
|
|
142 |
return self._mfg_data('U#')
|
|
143 |
|
|
144 |
def laptop_keyboard(self):
|
|
145 |
kb = self._mfg_data('KM') + '-'
|
|
146 |
kb += self._mfg_data('KL') + '-'
|
|
147 |
kb += self._mfg_data('KV')
|
|
148 |
return kb
|
|
149 |
|
|
150 |
def laptop_wireless_mac(self):
|
|
151 |
return self._mfg_data('WM')
|
|
152 |
|
|
153 |
def laptop_bios_version(self):
|
|
154 |
return self._mfg_data('BV')
|
|
155 |
|
|
156 |
def laptop_country(self):
|
|
157 |
return self._mfg_data('LA')
|
|
158 |
|
|
159 |
def laptop_localization(self):
|
|
160 |
return self._mfg_data('LO')
|
|
161 |
|
|
162 |
def _battery_info(self, item):
|
|
163 |
""" from /sys/class/power-supply/olpc-battery/ """
|
|
164 |
root = '/sys/class/power_supply/olpc-battery/'
|
|
165 |
if not os.path.exists(root+item):
|
|
166 |
return ''
|
|
167 |
|
|
168 |
return self.__read_file(root+item).strip()
|
|
169 |
|
|
170 |
def battery_serial_number(self):
|
|
171 |
return self._battery_info('serial_number')
|
|
172 |
|
|
173 |
def battery_capacity(self):
|
|
174 |
return self._battery_info('capacity') + ' ' + \
|
|
175 |
self._battery_info('capacity_level')
|
|
176 |
|
|
177 |
def battery_info(self):
|
|
178 |
#Should be just:
|
|
179 |
#return self._battery_info('uevent')
|
|
180 |
|
|
181 |
#But because of a bug in the kernel, that has trash, lets filter:
|
|
182 |
bi = ''
|
|
183 |
for line in self._battery_info('uevent').splitlines():
|
|
184 |
if line.startswith('POWER_'):
|
|
185 |
bi += line + '\n'
|
|
186 |
|
|
187 |
return bi
|
|
188 |
|
|
189 |
def disksize(self, path):
|
|
190 |
return os.statvfs(path).f_bsize * os.statvfs(path).f_blocks
|
|
191 |
|
|
192 |
def diskfree(self, path):
|
|
193 |
return os.statvfs(path).f_bsize * os.statvfs(path).f_bavail
|
|
194 |
|
|
195 |
def _read_popen(self, cmd):
|
|
196 |
p = os.popen(cmd)
|
|
197 |
s = ''
|
|
198 |
try:
|
|
199 |
for line in p:
|
|
200 |
s += line
|
|
201 |
finally:
|
|
202 |
p.close()
|
|
203 |
|
|
204 |
return s
|
|
205 |
|
|
206 |
def ifconfig(self):
|
|
207 |
return self._read_popen('/sbin/ifconfig')
|
|
208 |
|
|
209 |
def route_n(self):
|
|
210 |
return self._read_popen('/sbin/route -n')
|
|
211 |
|
|
212 |
def df_a(self):
|
|
213 |
return self._read_popen('/bin/df -a')
|
|
214 |
|
|
215 |
def ps_auxfwww(self):
|
|
216 |
return self._read_popen('/bin/ps auxfwww')
|
|
217 |
|
|
218 |
def usr_bin_free(self):
|
|
219 |
return self._read_popen('/usr/bin/free')
|
|
220 |
|
|
221 |
def top(self):
|
|
222 |
return self._read_popen('/usr/bin/top -bn2')
|
|
223 |
|
|
224 |
def installed_activities(self):
|
|
225 |
s = ''
|
|
226 |
for path in glob.glob('/usr/share/activities/*.activity'):
|
|
227 |
s += os.path.basename(path) + '\n'
|
|
228 |
|
|
229 |
for path in glob.glob('/home/olpc/Activities/*'):
|
|
230 |
s += '~' + os.path.basename(path) + '\n'
|
|
231 |
|
|
232 |
return s
|
|
233 |
|
|
234 |
|
|
235 |
|
|
236 |
class LogCollect:
|
|
237 |
"""Collect XO logfiles and machine metadata for reporting to OLPC
|
|
238 |
|
|
239 |
"""
|
|
240 |
def __init__(self):
|
|
241 |
self._mp = MachineProperties()
|
|
242 |
|
|
243 |
def write_logs(self, archive='', logbytes=15360):
|
|
244 |
"""Write a zipfile containing the tails of the logfiles and machine info of the XO
|
|
245 |
|
|
246 |
Arguments:
|
|
247 |
archive - Specifies the location where to store the data
|
|
248 |
defaults to /dev/shm/logs-<xo-serial>.zip
|
|
249 |
|
|
250 |
logbytes - Maximum number of bytes to read from each log file.
|
|
251 |
0 means complete logfiles, not just the tail
|
|
252 |
-1 means only save machine info, no logs
|
|
253 |
"""
|
|
254 |
#This function is crammed with try...except to make sure we get as much
|
|
255 |
#data as possible, if anything fails.
|
|
256 |
|
|
257 |
if archive=='':
|
|
258 |
archive = '/dev/shm/logs.zip'
|
|
259 |
try:
|
|
260 |
#With serial number is more convenient, but might fail for some
|
|
261 |
#Unknown reason...
|
|
262 |
archive = '/dev/shm/logs-%s.zip' % self._mp.laptop_serial_number()
|
|
263 |
except Exception:
|
|
264 |
pass
|
|
265 |
|
|
266 |
z = zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED)
|
|
267 |
|
|
268 |
try:
|
|
269 |
try:
|
|
270 |
z.writestr('info.txt', self.laptop_info())
|
|
271 |
except Exception, e:
|
|
272 |
z.writestr('info.txt',
|
|
273 |
"logcollect: could not add info.txt: %s" % e)
|
|
274 |
|
|
275 |
if logbytes > -1:
|
|
276 |
# Include some log files from /var/log.
|
|
277 |
for fn in ['dmesg', 'messages', 'cron', 'maillog','rpmpkgs',
|
|
278 |
'Xorg.0.log', 'spooler']:
|
|
279 |
try:
|
|
280 |
if os.access('/var/log/'+fn, os.F_OK):
|
|
281 |
if logbytes == 0:
|
|
282 |
z.write('/var/log/'+fn, 'var-log/'+fn)
|
|
283 |
else:
|
|
284 |
z.writestr('var-log/'+fn,
|
|
285 |
self.file_tail('/var/log/'+fn, logbytes))
|
|
286 |
except Exception, e:
|
|
287 |
z.writestr('var-log/'+fn,
|
|
288 |
"logcollect: could not add %s: %s" % (fn, e))
|
|
289 |
|
|
290 |
# Include all current ones from sugar/logs
|
|
291 |
for path in glob.glob('/home/olpc/.sugar/default/logs/*.log'):
|
|
292 |
try:
|
|
293 |
if os.access(path, os.F_OK):
|
|
294 |
if logbytes == 0:
|
|
295 |
z.write(path, 'sugar-logs/'+os.path.basename(path))
|
|
296 |
else:
|
|
297 |
z.writestr('sugar-logs/'+os.path.basename(path),
|
|
298 |
self.file_tail(path, logbytes))
|
|
299 |
except Exception, e:
|
|
300 |
z.writestr('sugar-logs/'+fn,
|
|
301 |
"logcollect: could not add %s: %s" % (fn, e))
|
|
302 |
try:
|
|
303 |
z.write('/etc/resolv.conf')
|
|
304 |
except Exception, e:
|
|
305 |
z.writestr('/etc/resolv.conf',
|
|
306 |
"logcollect: could not add resolv.conf: %s" % e)
|
|
307 |
|
|
308 |
except Exception, e:
|
|
309 |
print 'While creating zip archive: %s' % e
|
|
310 |
|
|
311 |
z.close()
|
|
312 |
|
|
313 |
return archive
|
|
314 |
|
|
315 |
def file_tail(self, filename, tailbytes):
|
|
316 |
"""Read the tail (end) of the file
|
|
317 |
|
|
318 |
Arguments:
|
|
319 |
filename The name of the file to read
|
|
320 |
tailbytes Number of bytes to include or 0 for entire file
|
|
321 |
"""
|
|
322 |
|
|
323 |
data = ''
|
|
324 |
|
|
325 |
f = open(filename)
|
|
326 |
try:
|
|
327 |
fsize = os.stat(filename).st_size
|
|
328 |
|
|
329 |
if tailbytes > 0 and fsize > tailbytes:
|
|
330 |
f.seek(-tailbytes, 2)
|
|
331 |
|
|
332 |
data = f.read()
|
|
333 |
finally:
|
|
334 |
f.close()
|
|
335 |
|
|
336 |
return data
|
|
337 |
|
|
338 |
|
|
339 |
def make_report(self, target='stdout'):
|
|
340 |
"""Create the report
|
|
341 |
|
|
342 |
Arguments:
|
|
343 |
target - where to save the logs, a path or stdout
|
|
344 |
|
|
345 |
"""
|
|
346 |
|
|
347 |
li = self.laptop_info()
|
|
348 |
for k, v in li.iteritems():
|
|
349 |
print k + ': ' +v
|
|
350 |
|
|
351 |
print self._mp.battery_info()
|
|
352 |
|
|
353 |
def laptop_info(self):
|
|
354 |
"""Return a string with laptop serial, battery type, build, memory info, etc."""
|
|
355 |
|
|
356 |
s = ''
|
|
357 |
try:
|
|
358 |
# Do not include UUID!
|
|
359 |
s += 'laptop-info-version: 1.0\n'
|
|
360 |
s += 'clock: %f\n' % time.clock()
|
|
361 |
s += 'date: %s' % time.strftime("%a, %d %b %Y %H:%M:%S +0000",
|
|
362 |
time.gmtime())
|
|
363 |
s += 'memfree: %s\n' % self._mp.memfree()
|
|
364 |
s += 'disksize: %s MB\n' % ( self._mp.disksize('/') / (1024*1024) )
|
|
365 |
s += 'diskfree: %s MB\n' % ( self._mp.diskfree('/') / (1024*1024) )
|
|
366 |
s += 'olpc_build: %s\n' % self._mp.olpc_build()
|
|
367 |
s += 'kernel_version: %s\n' % self._mp.kernel_version()
|
|
368 |
s += 'uptime: %s\n' % self._mp.uptime()
|
|
369 |
s += 'loadavg: %s\n' % self._mp.loadavg()
|
|
370 |
s += 'serial-number: %s\n' % self._mp.laptop_serial_number()
|
|
371 |
s += 'motherboard-number: %s\n' % self._mp.laptop_motherboard_number()
|
|
372 |
s += 'board-revision: %s\n' % self._mp.laptop_board_revision()
|
|
373 |
s += 'keyboard: %s\n' % self._mp.laptop_keyboard()
|
|
374 |
s += 'wireless_mac: %s\n' % self._mp.laptop_wireless_mac()
|
|
375 |
s += 'firmware: %s\n' % self._mp.laptop_bios_version()
|
|
376 |
s += 'country: %s\n' % self._mp.laptop_country()
|
|
377 |
s += 'localization: %s\n' % self._mp.laptop_localization()
|
|
378 |
|
|
379 |
s += self._mp.battery_info()
|
|
380 |
|
|
381 |
s += "\n[/sbin/ifconfig]\n%s\n" % self._mp.ifconfig()
|
|
382 |
s += "\n[/sbin/route -n]\n%s\n" % self._mp.route_n()
|
|
383 |
|
|
384 |
s += '\n[Installed Activities]\n%s\n' % self._mp.installed_activities()
|
|
385 |
|
|
386 |
s += '\n[df -a]\n%s\n' % self._mp.df_a()
|
|
387 |
s += '\n[ps auxwww]\n%s\n' % self._mp.ps_auxfwww()
|
|
388 |
s += '\n[free]\n%s\n' % self._mp.usr_bin_free()
|
|
389 |
s += '\n[top -bn2]\n%s\n' % self._mp.top()
|
|
390 |
except Exception, e:
|
|
391 |
s += '\nException while building info:\n%s\n' % e
|
|
392 |
|
|
393 |
return s
|
|
394 |
|
|
395 |
class LogSend:
|
|
396 |
|
|
397 |
# post_multipart and encode_multipart_formdata have been taken from
|
|
398 |
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
|
|
399 |
def post_multipart(self, host, selector, fields, files):
|
|
400 |
"""
|
|
401 |
Post fields and files to an http host as multipart/form-data.
|
|
402 |
fields is a sequence of (name, value) elements for regular form fields.
|
|
403 |
files is a sequence of (name, filename, value) elements for data to be uploaded as files
|
|
404 |
Return the server's response page.
|
|
405 |
"""
|
|
406 |
content_type, body = self.encode_multipart_formdata(fields, files)
|
|
407 |
h = httplib.HTTP(host)
|
|
408 |
h.putrequest('POST', selector)
|
|
409 |
h.putheader('content-type', content_type)
|
|
410 |
h.putheader('content-length', str(len(body)))
|
|
411 |
h.putheader('Host', host)
|
|
412 |
h.endheaders()
|
|
413 |
h.send(body)
|
|
414 |
errcode, errmsg, headers = h.getreply()
|
|
415 |
return h.file.read()
|
|
416 |
|
|
417 |
def encode_multipart_formdata(self, fields, files):
|
|
418 |
"""
|
|
419 |
fields is a sequence of (name, value) elements for regular form fields.
|
|
420 |
files is a sequence of (name, filename, value) elements for data to be uploaded as files
|
|
421 |
Return (content_type, body) ready for httplib.HTTP instance
|
|
422 |
"""
|
|
423 |
BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
|
|
424 |
CRLF = '\r\n'
|
|
425 |
L = []
|
|
426 |
for (key, value) in fields:
|
|
427 |
L.append('--' + BOUNDARY)
|
|
428 |
L.append('Content-Disposition: form-data; name="%s"' % key)
|
|
429 |
L.append('')
|
|
430 |
L.append(value)
|
|
431 |
for (key, filename, value) in files:
|
|
432 |
L.append('--' + BOUNDARY)
|
|
433 |
L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
|
|
434 |
L.append('Content-Type: %s' % self.get_content_type(filename))
|
|
435 |
L.append('')
|
|
436 |
L.append(value)
|
|
437 |
L.append('--' + BOUNDARY + '--')
|
|
438 |
L.append('')
|
|
439 |
body = CRLF.join(L)
|
|
440 |
content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
|
|
441 |
return content_type, body
|
|
442 |
|
|
443 |
def read_file(self, filename):
|
|
444 |
"""Read the entire contents of a file and return it as a string"""
|
|
445 |
|
|
446 |
data = ''
|
|
447 |
|
|
448 |
f = open(filename)
|
|
449 |
try:
|
|
450 |
data = f.read()
|
|
451 |
finally:
|
|
452 |
f.close()
|
|
453 |
|
|
454 |
return data
|
|
455 |
|
|
456 |
def get_content_type(self, filename):
|
|
457 |
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
|
458 |
|
|
459 |
def http_post_logs(self, url, archive):
|
|
460 |
#host, selector, fields, files
|
|
461 |
files = ('logs', os.path.basename(archive), self.read_file(archive)),
|
|
462 |
|
|
463 |
# Client= olpc will make the server return just "OK" or "FAIL"
|
|
464 |
fields = ('client', 'xo'),
|
|
465 |
urlparts = urlparse.urlsplit(url)
|
|
466 |
print "Sending logs to %s" % url
|
|
467 |
r = self.post_multipart(urlparts[1], urlparts[2], fields, files)
|
|
468 |
print r
|
|
469 |
return (r == 'OK')
|
|
470 |
|
|
471 |
|
|
472 |
# This script is dual-mode, it can be used as a command line tool and as
|
|
473 |
# a library.
|
|
474 |
if sys.argv[0].endswith('logcollect.py') or \
|
|
475 |
sys.argv[0].endswith('logcollect'):
|
|
476 |
print 'log-collect utility 1.0'
|
|
477 |
|
|
478 |
lc = LogCollect()
|
|
479 |
ls = LogSend()
|
|
480 |
|
|
481 |
logs = ''
|
|
482 |
mode = 'http'
|
|
483 |
|
|
484 |
if len(sys.argv)==1:
|
|
485 |
print """logcollect.py - send your XO logs to OLPC
|
|
486 |
|
|
487 |
Usage:
|
|
488 |
logcollect.py http - send logs to default server
|
|
489 |
|
|
490 |
logcollect.py http://server.name/submit.php
|
|
491 |
- submit logs to alternative server
|
|
492 |
|
|
493 |
logcollect.py file:/media/xxxx-yyyy/mylog.zip
|
|
494 |
- save the zip file on a USB device or SD card
|
|
495 |
|
|
496 |
logcollect.py all file:/media/xxxx-yyyy/mylog.zip
|
|
497 |
- Save to zip file and include ALL logs
|
|
498 |
|
|
499 |
logcollect.py none http
|
|
500 |
- Just send info.txt, but no logs via http.
|
|
501 |
|
|
502 |
logcollect.py none file
|
|
503 |
- Just save info.txt in /dev/shm/logs-SN123.zip
|
|
504 |
|
|
505 |
If you specify 'all' or 'none' you must specify http or file as well.
|
|
506 |
"""
|
|
507 |
sys.exit()
|
|
508 |
|
|
509 |
|
|
510 |
logbytes = 15360
|
|
511 |
if len(sys.argv)>1:
|
|
512 |
mode = sys.argv[len(sys.argv)-1]
|
|
513 |
if sys.argv[1] == 'all':
|
|
514 |
logbytes = 0
|
|
515 |
if sys.argv[1] == 'none':
|
|
516 |
logbytes = -1
|
|
517 |
|
|
518 |
|
|
519 |
if mode.startswith('file'):
|
|
520 |
# file://
|
|
521 |
logs = mode[5:]
|
|
522 |
|
|
523 |
#if mode.lower().startswith('http'):
|
|
524 |
# pass
|
|
525 |
#else if mode.lower().startswith('usb'):
|
|
526 |
# pass
|
|
527 |
#else if mode.lower().startswith('sd'):
|
|
528 |
# pass
|
|
529 |
|
|
530 |
logs = lc.write_logs(logs, logbytes)
|
|
531 |
print 'Logs saved in %s' % logs
|
|
532 |
|
|
533 |
sent_ok = False
|
|
534 |
if len(sys.argv)>1:
|
|
535 |
mode = sys.argv[len(sys.argv)-1]
|
|
536 |
|
|
537 |
if mode.startswith('http'):
|
|
538 |
print "Trying to send the logs using HTTP (web)"
|
|
539 |
if len(mode) == 4:
|
|
540 |
url = 'http://olpc.scheffers.net/olpc/submit.tcl'
|
|
541 |
else:
|
|
542 |
url = mode
|
|
543 |
|
|
544 |
if ls.http_post_logs(url, logs):
|
|
545 |
print "Logs were sent."
|
|
546 |
sent_ok = True
|
|
547 |
else:
|
|
548 |
print "FAILED to send logs."
|
|
549 |
|
|
550 |
|
|
551 |
if sent_ok:
|
|
552 |
os.remove(logs)
|
|
553 |
print "Logs were sent, tempfile deleted."
|
|
554 |
|
|
555 |
|