Codebase list sugar-log-activity / 70c1e84
Fix several flake8 errors and warnings Suppress E402 as unavoidable. James Cameron 6 years ago
4 changed file(s) with 170 addition(s) and 168 deletion(s). Raw diff Collapse all Expand all
0 [flake8]
1
2 # E402 module level import not at top of file
3 # gi.require_version() is required before later imports
4
5 ignore = E402
0 # Copyright (C) 2007, Pascal Scheffers <pascal@scheffers.net>
0 # Copyright (C) 2007, Pascal Scheffers <pascal@scheffers.net>
11 #
22 # Permission is hereby granted, free of charge, to any person
33 # obtaining a copy of this software and associated documentation
3232 # ** ...
3333 # * Installed packages list
3434 # * All relevant log files (all of them, at first)
35 #
35 #
3636 # The report is output as a tarfile
3737 #
3838 # This file has two modes:
7777 return '#/etc/issue not found'
7878
7979 # Needed, because we want to default to the first non blank line:
80 first_line = ''
80 first_line = ''
8181
8282 for line in self.__read_file('/etc/issue').splitlines():
8383 if line.lower().find('olpc build') > -1:
84 return line
84 return line
8585 if first_line == '':
86 first_line=line
86 first_line = line
8787
8888 return first_line
8989
9090 def uptime(self):
9191 for line in self.__read_file('/proc/uptime').splitlines():
9292 if line != '':
93 return line
93 return line
9494 return ''
9595
9696 def loadavg(self):
9797 for line in self.__read_file('/proc/loadavg').splitlines():
9898 if line != '':
99 return line
99 return line
100100 return ''
101101
102102 def kernel_version(self):
103103 for line in self.__read_file('/proc/version').splitlines():
104104 if line != '':
105 return line
105 return line
106106 return ''
107107
108108 def memfree(self):
113113 return line[8:].strip()
114114
115115 def _trim_null(self, v):
116 if v != '' and ord(v[len(v)-1]) == 0:
117 v = v[:len(v)-1]
116 if v != '' and ord(v[len(v) - 1]) == 0:
117 v = v[:len(v) - 1]
118118 return v
119119
120120 def _mfg_data(self, item):
125125 if os.path.exists(test_path + item):
126126 mfg_path = test_path + item
127127 break
128 if mfg_path == None:
128 if mfg_path is None:
129129 return ''
130130
131131 v = self._trim_null(self.__read_file(mfg_path))
141141 s = self._mfg_data('SG')[0:1]
142142 if s == '':
143143 return ''
144
144
145145 return '%02X' % ord(self._mfg_data('SG')[0:1])
146
147146
148147 def laptop_uuid(self):
149148 return self._mfg_data('U#')
186185
187186 def laptop_localization(self):
188187 return self._mfg_data('LO')
189
188
190189 def _battery_info(self, item):
191 """ from /sys/class/power-supply/olpc-battery/ """
192190 root = '/sys/class/power_supply/olpc-battery/'
193 if not os.path.exists(root+item):
191 if not os.path.exists(root + item):
194192 return ''
195
196 return self.__read_file(root+item).strip()
193
194 return self.__read_file(root + item).strip()
197195
198196 def battery_serial_number(self):
199197 return self._battery_info('serial_number')
200
198
201199 def battery_capacity(self):
202200 return self._battery_info('capacity') + ' ' + \
203 self._battery_info('capacity_level')
201 self._battery_info('capacity_level')
204202
205203 def battery_info(self):
206 #Should be just:
207 #return self._battery_info('uevent')
208
209 #But because of a bug in the kernel, that has trash, lets filter:
210 bi = ''
204 # Should be just:
205 # return self._battery_info('uevent')
206 # But because of a bug in the kernel, that has trash, lets filter:
207 bi = ''
211208 for line in self._battery_info('uevent').splitlines():
212209 if line.startswith('POWER_'):
213210 bi += line + '\n'
214
211
215212 return bi
216
213
217214 def disksize(self, path):
218215 return os.statvfs(path).f_bsize * os.statvfs(path).f_blocks
219
216
220217 def diskfree(self, path):
221218 return os.statvfs(path).f_bsize * os.statvfs(path).f_bavail
222
219
223220 def _read_popen(self, cmd):
224221 p = os.popen(cmd)
225222 s = ''
226223 try:
227224 for line in p:
228 s += line
225 s += line
229226 finally:
230 p.close()
231
227 p.close()
228
232229 return s
233
234 def ifconfig(self):
230
231 def ifconfig(self):
235232 return self._read_popen('/sbin/ifconfig')
236
237 def route_n(self):
233
234 def route_n(self):
238235 return self._read_popen('/sbin/route -n')
239
236
240237 def df_a(self):
241238 return self._read_popen('/bin/df -a')
242
239
243240 def ps_auxfwww(self):
244241 return self._read_popen('/bin/ps auxfwww')
245
242
246243 def usr_bin_free(self):
247244 return self._read_popen('/usr/bin/free')
248245
249246 def top(self):
250247 return self._read_popen('/usr/bin/top -bn2')
251
252 def installed_activities(self):
253 s = ''
248
249 def installed_activities(self):
250 s = ''
254251 for path in glob.glob('/usr/share/sugar/activities/*.activity'):
255252 s += os.path.basename(path) + '\n'
256253
257254 home = os.path.expanduser('~')
258255 for path in glob.glob(os.path.join(home, 'Activities', '*')):
259256 s += '~' + os.path.basename(path) + '\n'
260
257
261258 return s
262
263
259
264260
265261 class LogCollect:
266262 """Collect XO logfiles and machine metadata for reporting to OLPC
270266 self._mp = MachineProperties()
271267
272268 def write_logs(self, archive='', logbytes=15360):
273 """Write a zipfile containing the tails of the logfiles and machine info of the XO
274
269 """Write a zipfile containing the tails of the logfiles and
270 machine info of the XO
271
275272 Arguments:
276273 archive - Specifies the location where to store the data
277274 defaults to /dev/shm/logs-<xo-serial>.zip
278
275
279276 logbytes - Maximum number of bytes to read from each log file.
280277 0 means complete logfiles, not just the tail
281278 -1 means only save machine info, no logs
282279 """
283 #This function is crammed with try...except to make sure we get as much
284 #data as possible, if anything fails.
285
286 if archive=='':
280 # This function is crammed with try...except to make sure we
281 # get as much data as possible, if anything fails.
282
283 if archive == '':
287284 archive = '/dev/shm/logs.zip'
288285 try:
289 #With serial number is more convenient, but might fail for some
290 #Unknown reason...
291 archive = '/dev/shm/logs-%s.zip' % self._mp.laptop_serial_number()
286 # With serial number is more convenient, but might
287 # fail for some reason...
288 archive = '/dev/shm/logs-%s.zip' % \
289 self._mp.laptop_serial_number()
292290 except Exception:
293291 pass
294
292
295293 z = zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED)
296
297 try:
298 try:
294
295 try:
296 try:
299297 z.writestr('info.txt', self.laptop_info())
300298 except Exception, e:
301299 z.writestr('info.txt',
302300 "logcollect: could not add info.txt: %s" % e)
303
304 if logbytes > -1:
301
302 if logbytes > -1:
305303 # Include some log files from /var/log.
306 for fn in ['dmesg', 'messages', 'cron', 'maillog','rpmpkgs',
304 for fn in ['dmesg', 'messages', 'cron', 'maillog', 'rpmpkgs',
307305 'Xorg.0.log', 'spooler']:
308306 try:
309 if os.access('/var/log/'+fn, os.F_OK):
307 if os.access('/var/log/' + fn, os.F_OK):
310308 if logbytes == 0:
311 z.write('/var/log/'+fn, 'var-log/'+fn)
309 z.write('/var/log/' + fn, 'var-log/' + fn)
312310 else:
313 z.writestr('var-log/'+fn,
314 self.file_tail('/var/log/'+fn, logbytes))
311 z.writestr('var-log/' + fn,
312 self.file_tail('/var/log/' + fn,
313 logbytes))
315314 except Exception, e:
316 z.writestr('var-log/'+fn,
317 "logcollect: could not add %s: %s" % (fn, e))
315 z.writestr('var-log/' + fn,
316 "logcollect: could not add %s: %s" %
317 (fn, e))
318318
319319 home = os.path.expanduser('~')
320320 here = os.path.join(home, '.sugar/default/logs/*.log')
330330 self.file_tail(path, logbytes))
331331 except Exception, e:
332332 z.writestr(name,
333 "logcollect: could not add %s: %s" % (name, e))
333 "logcollect: could not add %s: %s" %
334 (name, e))
334335 here = os.path.join(home, '.sugar/default/logs/*/*.log')
335336 for path in glob.glob(here):
336337 if os.access(path, os.F_OK):
345346 self.file_tail(path, logbytes))
346347 except Exception, e:
347348 z.writestr(name,
348 "logcollect: could not add %s: %s" % (name, e))
349 try:
349 "logcollect: could not add %s: %s" %
350 (name, e))
351 try:
350352 z.write('/etc/resolv.conf')
351353 except Exception, e:
352354 z.writestr('/etc/resolv.conf',
353355 "logcollect: could not add resolv.conf: %s" % e)
354
356
355357 except Exception, e:
356 print 'While creating zip archive: %s' % e
357
358 z.close()
359
358 print 'While creating zip archive: %s' % e
359
360 z.close()
361
360362 return archive
361363
362364 def file_tail(self, filename, tailbytes):
363365 """Read the tail (end) of the file
364
366
365367 Arguments:
366368 filename The name of the file to read
367369 tailbytes Number of bytes to include or 0 for entire file
372374 f = open(filename)
373375 try:
374376 fsize = os.stat(filename).st_size
375
377
376378 if tailbytes > 0 and fsize > tailbytes:
377379 f.seek(-tailbytes, 2)
378
380
379381 data = f.read()
380382 finally:
381383 f.close()
382384
383 return data
384
385 return data
385386
386387 def make_report(self, target='stdout'):
387388 """Create the report
388389
389390 Arguments:
390 target - where to save the logs, a path or stdout
391 target - where to save the logs, a path or stdout
391392
392393 """
393394
394395 li = self.laptop_info()
395396 for k, v in li.iteritems():
396 print k + ': ' +v
397
397 print k + ': ' + v
398
398399 print self._mp.battery_info()
399400
400401 def laptop_info(self):
401 """Return a string with laptop serial, battery type, build, memory info, etc."""
402
403 s = ''
402 """Return a string with laptop serial, battery type, build,
403 memory info, etc."""
404
405 s = ''
404406 try:
405407 # Do not include UUID!
406408 s += 'laptop-info-version: 1.0\n'
407409 s += 'clock: %f\n' % time.clock()
408410 s += 'date: %s\n' % time.strftime("%a, %d %b %Y %H:%M:%S +0000",
409 time.gmtime())
411 time.gmtime())
410412 s += 'memfree: %s\n' % self._mp.memfree()
411 s += 'disksize: %s MB\n' % ( self._mp.disksize('/') / (1024*1024) )
412 s += 'diskfree: %s MB\n' % ( self._mp.diskfree('/') / (1024*1024) )
413 s += 'disksize: %s MB\n' % (self._mp.disksize('/') / (1024 * 1024))
414 s += 'diskfree: %s MB\n' % (self._mp.diskfree('/') / (1024 * 1024))
413415 s += 'olpc_build: %s\n' % self._mp.olpc_build()
414416 s += 'kernel_version: %s\n' % self._mp.kernel_version()
415417 s += 'uptime: %s\n' % self._mp.uptime()
416 s += 'loadavg: %s\n' % self._mp.loadavg()
418 s += 'loadavg: %s\n' % self._mp.loadavg()
417419 s += 'serial-number: %s\n' % self._mp.laptop_serial_number()
418 s += 'motherboard-number: %s\n' % self._mp.laptop_motherboard_number()
419 s += 'board-revision: %s\n' % self._mp.laptop_board_revision()
420 s += 'keyboard: %s\n' % self._mp.laptop_keyboard()
421 s += 'wireless_mac: %s\n' % self._mp.laptop_wireless_mac()
422 s += 'firmware: %s\n' % self._mp.laptop_bios_version()
420 s += 'motherboard-number: %s\n' % \
421 self._mp.laptop_motherboard_number()
422 s += 'board-revision: %s\n' % self._mp.laptop_board_revision()
423 s += 'keyboard: %s\n' % self._mp.laptop_keyboard()
424 s += 'wireless_mac: %s\n' % self._mp.laptop_wireless_mac()
425 s += 'firmware: %s\n' % self._mp.laptop_bios_version()
423426 s += 'country: %s\n' % self._mp.laptop_country()
424427 s += 'localization: %s\n' % self._mp.laptop_localization()
425
428
426429 s += self._mp.battery_info()
427
430
428431 s += "\n[/sbin/ifconfig]\n%s\n" % self._mp.ifconfig()
429432 s += "\n[/sbin/route -n]\n%s\n" % self._mp.route_n()
430
431 s += '\n[Installed Activities]\n%s\n' % self._mp.installed_activities()
432
433
434 s += '\n[Installed Activities]\n%s\n' % \
435 self._mp.installed_activities()
436
433437 s += '\n[df -a]\n%s\n' % self._mp.df_a()
434438 s += '\n[ps auxwww]\n%s\n' % self._mp.ps_auxfwww()
435439 s += '\n[free]\n%s\n' % self._mp.usr_bin_free()
436440 s += '\n[top -bn2]\n%s\n' % self._mp.top()
437441 except Exception, e:
438442 s += '\nException while building info:\n%s\n' % e
439
443
440444 return s
441445
446
442447 class LogSend:
443
448
444449 # post_multipart and encode_multipart_formdata have been taken from
445450 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
446451 def post_multipart(self, host, selector, fields, files):
447452 """
448453 Post fields and files to an http host as multipart/form-data.
449 fields is a sequence of (name, value) elements for regular form fields.
450 files is a sequence of (name, filename, value) elements for data to be uploaded as files
451 Return the server's response page.
454 fields is a sequence of (name, value) elements for regular
455 form fields. files is a sequence of (name, filename, value)
456 elements for data to be uploaded as files Return the server's
457 response page.
452458 """
453459 content_type, body = self.encode_multipart_formdata(fields, files)
454460 h = httplib.HTTP(host)
460466 h.send(body)
461467 errcode, errmsg, headers = h.getreply()
462468 return h.file.read()
463
469
464470 def encode_multipart_formdata(self, fields, files):
465471 """
466 fields is a sequence of (name, value) elements for regular form fields.
467 files is a sequence of (name, filename, value) elements for data to be uploaded as files
468 Return (content_type, body) ready for httplib.HTTP instance
472 fields is a sequence of (name, value) elements for regular
473 form fields. files is a sequence of (name, filename, value)
474 elements for data to be uploaded as files Return
475 (content_type, body) ready for httplib.HTTP instance
469476 """
470477 BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
471478 CRLF = '\r\n'
477484 L.append(value)
478485 for (key, filename, value) in files:
479486 L.append('--' + BOUNDARY)
480 L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
487 L.append('Content-Disposition: form-data; '
488 'name="%s"; filename="%s"' % (key, filename))
481489 L.append('Content-Type: %s' % self.get_content_type(filename))
482490 L.append('')
483491 L.append(value)
486494 body = CRLF.join(L)
487495 content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
488496 return content_type, body
489
497
490498 def read_file(self, filename):
491499 """Read the entire contents of a file and return it as a string"""
492500
502510
503511 def get_content_type(self, filename):
504512 return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
505
513
506514 def http_post_logs(self, url, archive):
507 #host, selector, fields, files
515 # host, selector, fields, files
508516 files = ('logs', os.path.basename(archive), self.read_file(archive)),
509
517
510518 # Client= olpc will make the server return just "OK" or "FAIL"
511519 fields = ('client', 'xo'),
512520 urlparts = urlparse.urlsplit(url)
517525
518526
519527 # This script is dual-mode, it can be used as a command line tool and as
520 # a library.
528 # a library.
521529 if sys.argv[0].endswith('logcollect.py') or \
522530 sys.argv[0].endswith('logcollect'):
523531 print 'log-collect utility 1.0'
524
532
525533 lc = LogCollect()
526534 ls = LogSend()
527535
528536 logs = ''
529537 mode = 'http'
530
531 if len(sys.argv)==1:
538
539 if len(sys.argv) == 1:
532540 print """logcollect.py - send your XO logs to OLPC
533
541
534542 Usage:
535543 logcollect.py http://server.name/submit.php
536544 - submit logs to a server
537
545
538546 logcollect.py file:/media/xxxx-yyyy/mylog.zip
539547 - save the zip file on a USB device or SD card
540
548
541549 logcollect.py all file:/media/xxxx-yyyy/mylog.zip
542550 - Save to zip file and include ALL logs
543551
550558 If you specify 'all' or 'none' you must specify http or file as well.
551559 """
552560 sys.exit()
553
554
555 logbytes = 15360
556 if len(sys.argv)>1:
557 mode = sys.argv[len(sys.argv)-1]
561
562 logbytes = 15360
563 if len(sys.argv) > 1:
564 mode = sys.argv[len(sys.argv) - 1]
558565 if sys.argv[1] == 'all':
559566 logbytes = 0
560567 if sys.argv[1] == 'none':
561568 logbytes = -1
562
563569
564570 if mode.startswith('file'):
565571 # file://
566572 logs = mode[5:]
567
568 #if mode.lower().startswith('http'):
569 # pass
570 #else if mode.lower().startswith('usb'):
571 # pass
572 #else if mode.lower().startswith('sd'):
573 # pass
574
573
575574 logs = lc.write_logs(logs, logbytes)
576575 print 'Logs saved in %s' % logs
577
576
578577 sent_ok = False
579 if len(sys.argv)>1:
580 mode = sys.argv[len(sys.argv)-1]
581
578 if len(sys.argv) > 1:
579 mode = sys.argv[len(sys.argv) - 1]
580
582581 if mode.startswith('http'):
583582 print "Trying to send the logs using HTTP (web)"
584583 if len(mode) == 4:
586585 sys.exit(1)
587586 else:
588587 url = mode
589
588
590589 if ls.http_post_logs(url, logs):
591590 print "Logs were sent."
592 sent_ok = True
591 sent_ok = True
593592 else:
594593 print "FAILED to send logs."
595
596594
597595 if sent_ok:
598596 os.remove(logs)
599597 print "Logs were sent, tempfile deleted."
600
601
109109 self.extra_iter = self._treemodel.append(None, [_('Other'), ''])
110110
111111 self.list_scroll = Gtk.ScrolledWindow()
112 self.list_scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
112 self.list_scroll.set_policy(Gtk.PolicyType.AUTOMATIC,
113 Gtk.PolicyType.AUTOMATIC)
113114 self.list_scroll.add(self._treeview)
114115 self.list_scroll.set_size_request(Gdk.Screen.width() * 30 / 100, -1)
115116
136137 def _sort_logfile(self, treemodel, itera, iterb, user_data=None):
137138 a = treemodel.get_value(itera, 0)
138139 b = treemodel.get_value(iterb, 0)
139 if a == None or b == None:
140 if a is None or b is None:
140141 return 0
141142 a = a.lower()
142143 b = b.lower()
224225 files = os.listdir(path)
225226 except:
226227 logging.debug(
227 _("ERROR: Failed to look for files in '%(path)s'.") %
228 {'path': path})
228 _("ERROR: Failed to look for files in '%(path)s'.") %
229 {'path': path})
229230 else:
230231 for logfile in files:
231232 self._add_log_file(os.path.join(path, logfile))
245246
246247 if not os.path.exists(path):
247248 logging.debug(_("ERROR: File '%(file)s' does not exist.") %
248 {'file': path})
249 {'file': path})
249250 return False
250251
251252 if not os.access(path, os.R_OK):
252253 logging.debug(_("ERROR: Unable to read file '%(file)s'.") %
253 {'file': path})
254 {'file': path})
254255 return False
255256
256257 directory, logfile = os.path.split(path)
259260 if _dir:
260261 logfile = '%s/%s' % (_dir, logfile)
261262
262 if not logfile in self.logs or _dir:
263 if logfile not in self.logs or _dir:
263264 if not parent:
264265 parent = self.extra_iter
265266 if directory in self.path_iter:
274275 log.update()
275276 written = log._written
276277
277 if self.active_log == None:
278 if self.active_log is None:
278279 self.active_log = log
279280 self._show_log(logfile)
280281 success, log_iter = \
397398 self._written = (self._pos - init_pos)
398399 except:
399400 self.insert(self.get_end_iter(),
400 _("Error: Can't open file '%s'\n") % self.logfile)
401 _("Error: Can't open file '%s'\n") % self.logfile)
401402 self._written = 0
402403
403404
468469 self.search_entry = iconentry.IconEntry()
469470 self.search_entry.set_size_request(Gdk.Screen.width() / 3, -1)
470471 self.search_entry.set_icon_from_name(
471 iconentry.ICON_ENTRY_PRIMARY, 'entry-search')
472 iconentry.ICON_ENTRY_PRIMARY, 'entry-search')
472473 self.search_entry.add_clear_button()
473474 self.search_entry.connect('activate', self._search_entry_activate_cb)
474475 self.search_entry.connect('changed', self._search_entry_changed_cb)
528529 self._add_controls(self._toolbar)
529530
530531 for control in [self._delete_btn, self._separator, self._stop_btn]:
531 if not control in self._toolbar:
532 if control not in self._toolbar:
532533 self._toolbar.insert(control, -1)
533534
534535 def _remove_controls(self, toolbar):
540541 def _add_controls(self, toolbar):
541542 for control in [self._search_item, self._search_prev,
542543 self._search_next]:
543 if not control in toolbar:
544 if control not in toolbar:
544545 toolbar.insert(control, -1)
545546 control.show()
546547
570571 if self._autosearch_timer:
571572 GObject.source_remove(self._autosearch_timer)
572573 self._autosearch_timer = GObject.timeout_add(_AUTOSEARCH_TIMEOUT,
573 self.__autosearch_timer_cb)
574
575 def __autosearch_timer_cb(self):
574 self.__autosearch_cb)
575
576 def __autosearch_cb(self):
576577 self._autosearch_timer = None
577578 self.search_entry.activate()
578579 return False
592593 else:
593594 prev_result = self.viewer.get_next_result('backward')
594595 next_result = self.viewer.get_next_result('forward')
595 self._search_prev.props.sensitive = prev_result != None
596 self._search_next.props.sensitive = next_result != None
596 self._search_prev.props.sensitive = prev_result is not None
597 self._search_next.props.sensitive = next_result is not None
597598
598599 def _delete_log_cb(self, widget):
599600 if self.viewer.active_log:
620621
621622 self._collector = LogCollect()
622623
623 label = Gtk.Label(label=
624 _('This captures information about the system\n'\
625 'and running processes to a journal entry.\n'\
626 'Use this to improve a problem report.'))
624 trans = _('This captures information about the system\n'
625 'and running processes to a journal entry.\n'
626 'Use this to improve a problem report.')
627 label = Gtk.Label(label=trans)
627628
628629 send_button = Gtk.Button(_('Capture information'))
629630 send_button.connect('clicked', self._on_send_button_clicked_cb)
669670 'title_set_by_user': '0',
670671 'suggested_filename': filename,
671672 'mime_type': 'application/zip',
672 }
673 }
673674 for k, v in metadata.items():
674675 jobject.metadata[k] = v
675676 jobject.file_path = filepath
1818 from sugar3.activity import bundlebuilder
1919
2020 bundlebuilder.start()
21