From ba906b7e3746bf0734b78d081868bd0f81828e30 Mon Sep 17 00:00:00 2001
From: Wade Brainerd <wadetb@gmail.com>
Date: Sun, 27 Sep 2009 21:14:18 -0400
Subject: [PATCH] Option 1 (not working): Upload logs button displays Report a problem control panel.

---
 logcollect.py |  554 ---------------------------------------------------------
 logviewer.py  |   70 ++------
 2 files changed, 11 insertions(+), 613 deletions(-)
 delete mode 100644 logcollect.py

diff --git a/logcollect.py b/logcollect.py
deleted file mode 100644
index 8713048..0000000
--- a/logcollect.py
+++ /dev/null
@@ -1,554 +0,0 @@
-# Copyright (C) 2007, Pascal Scheffers <pascal@scheffers.net> 
-#
-# Permission is hereby granted, free of charge, to any person
-# obtaining a copy of this software and associated documentation
-# files (the "Software"), to deal in the Software without
-# restriction, including without limitation the rights to use,
-# copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following
-# conditions:
-#
-# The above copyright notice and this permission notice shall be
-# included in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-# OTHER DEALINGS IN THE SOFTWARE.
-#
-# log-collect for OLPC
-#
-# Compile a report containing:
-#  * Basic system information:
-#  ** Serial number
-#  ** Battery type
-#  ** Build number
-#  ** Uptime
-#  ** disk free space
-#  ** ...
-#  * Installed packages list
-#  * All relevant log files (all of them, at first)
-# 
-# The report is output as a tarfile
-#
-# This file has two modes:
-# 1. It is a stand-alone python script, when invoked as 'log-collect'
-# 2. It is a python module.
-
-import os
-import zipfile
-import glob
-import sys
-import time
-
-# The next couple are used by LogSend
-import httplib
-import mimetypes
-import urlparse
-
-class MachineProperties:
-    """Various machine properties in easy to access chunks.
-    """
-
-    def __read_file(self, filename):
-        """Read the entire contents of a file and return it as a string"""
-
-        data = ''
-
-        f = open(filename)
-        try:
-            data = f.read()
-        finally:
-            f.close()
-
-        return data
-
-    def olpc_build(self):
-        """Buildnumber, from /etc/issue"""
-        # Is there a better place to get the build number?
-        if not os.path.exists('/etc/issue'):
-            return '#/etc/issue not found'
-
-        # Needed, because we want to default to the first non blank line:
-        first_line = '' 
-
-        for line in self.__read_file('/etc/issue').splitlines():
-            if line.lower().find('olpc build') > -1:
-                return line                
-            if first_line == '':
-                first_line=line
-
-        return first_line
-
-    def uptime(self):
-        for line in self.__read_file('/proc/uptime').splitlines():
-            if line != '':
-                return line            
-        return ''
-
-    def loadavg(self):
-        for line in self.__read_file('/proc/loadavg').splitlines():
-            if line != '':
-                return line            
-        return ''
-
-    def kernel_version(self):
-        for line in self.__read_file('/proc/version').splitlines():
-            if line != '':
-                return line            
-        return ''
-
-    def memfree(self):
-        line = ''
-
-        for line in self.__read_file('/proc/meminfo').splitlines():
-            if line.find('MemFree:') > -1:
-                return line[8:].strip()
-
-    def _mfg_data(self, item):
-        """Return mfg data item from /ofw/mfg-data/"""
-        
-        if not os.path.exists('/ofw/mfg-data/'+item):
-            return ''
-        
-        v = self.__read_file('/ofw/mfg-data/'+item)
-        # Remove trailing 0 character, if any:
-        if v != '' and ord(v[len(v)-1]) == 0:
-            v = v[:len(v)-1]
-        
-        return v
-            
-    def laptop_serial_number(self):
-        return self._mfg_data('SN')
-
-    def laptop_motherboard_number(self):
-        return self._mfg_data('B#')
-
-    def laptop_board_revision(self):
-        s = self._mfg_data('SG')[0:1]
-        if s == '':
-            return ''
-            
-        return '%02X' % ord(self._mfg_data('SG')[0:1])
-        
-
-    def laptop_uuid(self):
-        return self._mfg_data('U#')
-
-    def laptop_keyboard(self):
-        kb = self._mfg_data('KM') + '-'
-        kb += self._mfg_data('KL') + '-'
-        kb += self._mfg_data('KV')
-        return kb
-
-    def laptop_wireless_mac(self):
-        return self._mfg_data('WM')
-
-    def laptop_bios_version(self):
-        return self._mfg_data('BV')
-
-    def laptop_country(self):
-        return self._mfg_data('LA')
-
-    def laptop_localization(self):
-        return self._mfg_data('LO')
-    
-    def _battery_info(self, item):
-        """ from  /sys/class/power-supply/olpc-battery/ """
-        root = '/sys/class/power_supply/olpc-battery/'
-        if not os.path.exists(root+item):
-            return ''
-        
-        return self.__read_file(root+item).strip()
-
-    def battery_serial_number(self):
-        return self._battery_info('serial_number')
-    
-    def battery_capacity(self):
-        return self._battery_info('capacity') + ' ' + \
-                    self._battery_info('capacity_level')
-
-    def battery_info(self):
-        #Should be just:
-        #return self._battery_info('uevent')
-        
-        #But because of a bug in the kernel, that has trash, lets filter:
-        bi = ''        
-        for line in self._battery_info('uevent').splitlines():
-            if line.startswith('POWER_'):
-                bi += line + '\n'
-        
-        return bi
-       
-    def disksize(self, path):
-        return os.statvfs(path).f_bsize * os.statvfs(path).f_blocks
-    
-    def diskfree(self, path):
-        return os.statvfs(path).f_bsize * os.statvfs(path).f_bavail
-        
-    def _read_popen(self, cmd):
-        p = os.popen(cmd)
-        s = ''
-        try:
-            for line in p:
-                s += line 
-        finally:
-            p.close()        
-        
-        return s
-    
-    def ifconfig(self):        
-        return self._read_popen('/sbin/ifconfig')
-               
-    def route_n(self):        
-        return self._read_popen('/sbin/route -n')
-    
-    def df_a(self):
-        return self._read_popen('/bin/df -a')
-  
-    def ps_auxfwww(self):
-        return self._read_popen('/bin/ps auxfwww')
-    
-    def usr_bin_free(self):
-        return self._read_popen('/usr/bin/free')
-
-    def top(self):
-        return self._read_popen('/usr/bin/top -bn2')
-        
-    def installed_activities(self):        
-        s = ''        
-        for path in glob.glob('/usr/share/activities/*.activity'):
-            s += os.path.basename(path) + '\n'
-
-        for path in glob.glob('/home/olpc/Activities/*'):
-            s += '~' + os.path.basename(path) + '\n'
-            
-        return s
-        
-        
-
-class LogCollect:
-    """Collect XO logfiles and machine metadata for reporting to OLPC
-
-    """
-    def __init__(self):
-        self._mp = MachineProperties()
-
-    def write_logs(self, archive='', logbytes=15360):
-        """Write a zipfile containing the tails of the logfiles and machine info of the XO
-        
-        Arguments:
-            archive -   Specifies the location where to store the data
-                        defaults to /dev/shm/logs-<xo-serial>.zip
-                        
-            logbytes -  Maximum number of bytes to read from each log file.
-                        0 means complete logfiles, not just the tail
-                        -1 means only save machine info, no logs
-        """
-        #This function is crammed with try...except to make sure we get as much
-        #data as possible, if anything fails.
-        
-        if archive=='':
-            archive = '/dev/shm/logs.zip'
-            try:
-                #With serial number is more convenient, but might fail for some
-                #Unknown reason...
-                archive = '/dev/shm/logs-%s.zip' % self._mp.laptop_serial_number()
-            except Exception:
-                pass
-            
-        z = zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED)
-        
-        try:            
-            try: 
-                z.writestr('info.txt', self.laptop_info())
-            except Exception, e:
-                z.writestr('info.txt',
-                           "logcollect: could not add info.txt: %s" % e)
-            
-            if logbytes > -1:            
-                # Include some log files from /var/log.
-                for fn in ['dmesg', 'messages', 'cron', 'maillog','rpmpkgs',
-                           'Xorg.0.log', 'spooler']:
-                    try:
-                        if os.access('/var/log/'+fn, os.F_OK):
-                            if logbytes == 0:
-                                z.write('/var/log/'+fn, 'var-log/'+fn)
-                            else:
-                                z.writestr('var-log/'+fn,
-                                           self.file_tail('/var/log/'+fn, logbytes))
-                    except Exception, e:
-                        z.writestr('var-log/'+fn,
-                                   "logcollect: could not add %s: %s" % (fn, e))
-                        
-                # Include all current ones from sugar/logs
-                for path in glob.glob('/home/olpc/.sugar/default/logs/*.log'):
-                    try:
-                        if os.access(path, os.F_OK):
-                            if logbytes == 0:
-                                z.write(path, 'sugar-logs/'+os.path.basename(path))
-                            else:
-                                z.writestr('sugar-logs/'+os.path.basename(path),
-                                           self.file_tail(path, logbytes))
-                    except Exception, e:
-                        z.writestr('sugar-logs/'+fn,
-                                   "logcollect: could not add %s: %s" % (fn, e))
-                try:                 
-                    z.write('/etc/resolv.conf')
-                except Exception, e:
-                    z.writestr('/etc/resolv.conf',
-                               "logcollect: could not add resolv.conf: %s" % e)
-                    
-        except Exception, e:
-            print 'While creating zip archive: %s' % e            
-        
-        z.close()        
-        
-        return archive
-
-    def file_tail(self, filename, tailbytes):
-        """Read the tail (end) of the file
-        
-        Arguments:
-            filename    The name of the file to read
-            tailbytes   Number of bytes to include or 0 for entire file
-        """
-
-        data = ''
-
-        f = open(filename)
-        try:
-            fsize = os.stat(filename).st_size
-            
-            if tailbytes > 0 and fsize > tailbytes:
-                f.seek(-tailbytes, 2)
-                
-            data = f.read()
-        finally:
-            f.close()
-
-        return data        
-              
-
-    def make_report(self, target='stdout'):
-        """Create the report
-
-        Arguments:
-            target - where to save the logs, a path or stdout            
-
-        """
-
-        li = self.laptop_info()
-        for k, v in li.iteritems():
-            print k + ': ' +v
-            
-        print self._mp.battery_info()
-
-    def laptop_info(self):
-        """Return a string with laptop serial, battery type, build, memory info, etc."""
-
-        s = ''        
-        try:
-            # Do not include UUID!
-            s += 'laptop-info-version: 1.0\n'
-            s += 'clock: %f\n' % time.clock()
-            s += 'date: %s' % time.strftime("%a, %d %b %Y %H:%M:%S +0000",
-                                            time.gmtime())
-            s += 'memfree: %s\n' % self._mp.memfree()
-            s += 'disksize: %s MB\n' % ( self._mp.disksize('/') / (1024*1024) ) 
-            s += 'diskfree: %s MB\n' % ( self._mp.diskfree('/') / (1024*1024) ) 
-            s += 'olpc_build: %s\n' % self._mp.olpc_build()
-            s += 'kernel_version: %s\n' % self._mp.kernel_version()
-            s += 'uptime: %s\n' % self._mp.uptime()
-            s += 'loadavg: %s\n' % self._mp.loadavg()        
-            s += 'serial-number: %s\n' % self._mp.laptop_serial_number()
-            s += 'motherboard-number: %s\n' % self._mp.laptop_motherboard_number()
-            s += 'board-revision: %s\n' %  self._mp.laptop_board_revision()
-            s += 'keyboard: %s\n' %  self._mp.laptop_keyboard()
-            s += 'wireless_mac: %s\n' %  self._mp.laptop_wireless_mac()
-            s += 'firmware: %s\n' %  self._mp.laptop_bios_version()
-            s += 'country: %s\n' % self._mp.laptop_country()
-            s += 'localization: %s\n' % self._mp.laptop_localization()
-                
-            s += self._mp.battery_info()
-            
-            s += "\n[/sbin/ifconfig]\n%s\n" % self._mp.ifconfig()
-            s += "\n[/sbin/route -n]\n%s\n" % self._mp.route_n()
-            
-            s += '\n[Installed Activities]\n%s\n' % self._mp.installed_activities()
-    
-            s += '\n[df -a]\n%s\n' % self._mp.df_a()
-            s += '\n[ps auxwww]\n%s\n' % self._mp.ps_auxfwww()
-            s += '\n[free]\n%s\n' % self._mp.usr_bin_free()
-            s += '\n[top -bn2]\n%s\n' % self._mp.top()
-        except Exception, e:
-            s += '\nException while building info:\n%s\n' % e
-        
-        return s
-
-class LogSend:
-    
-    # post_multipart and encode_multipart_formdata have been taken from
-    #  http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
-    def post_multipart(self, host, selector, fields, files):
-        """
-        Post fields and files to an http host as multipart/form-data.
-        fields is a sequence of (name, value) elements for regular form fields.
-        files is a sequence of (name, filename, value) elements for data to be uploaded as files
-        Return the server's response page.        
-        """
-        content_type, body = self.encode_multipart_formdata(fields, files)
-        h = httplib.HTTP(host)
-        h.putrequest('POST', selector)
-        h.putheader('content-type', content_type)
-        h.putheader('content-length', str(len(body)))
-        h.putheader('Host', host)
-        h.endheaders()
-        h.send(body)
-        errcode, errmsg, headers = h.getreply()
-        return h.file.read()
-    
-    def encode_multipart_formdata(self, fields, files):
-        """
-        fields is a sequence of (name, value) elements for regular form fields.
-        files is a sequence of (name, filename, value) elements for data to be uploaded as files
-        Return (content_type, body) ready for httplib.HTTP instance
-        """
-        BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$'
-        CRLF = '\r\n'
-        L = []
-        for (key, value) in fields:
-            L.append('--' + BOUNDARY)
-            L.append('Content-Disposition: form-data; name="%s"' % key)
-            L.append('')
-            L.append(value)
-        for (key, filename, value) in files:
-            L.append('--' + BOUNDARY)
-            L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
-            L.append('Content-Type: %s' % self.get_content_type(filename))
-            L.append('')
-            L.append(value)
-        L.append('--' + BOUNDARY + '--')
-        L.append('')
-        body = CRLF.join(L)
-        content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
-        return content_type, body
-    
-    def read_file(self, filename):
-        """Read the entire contents of a file and return it as a string"""
-
-        data = ''
-
-        f = open(filename)
-        try:
-            data = f.read()
-        finally:
-            f.close()
-
-        return data
-
-    def get_content_type(self, filename):
-        return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
-        
-    def http_post_logs(self, url, archive):
-        #host, selector, fields, files
-        files = ('logs', os.path.basename(archive), self.read_file(archive)),
-        
-        # Client= olpc will make the server return just "OK" or "FAIL"
-        fields = ('client', 'xo'),
-        urlparts = urlparse.urlsplit(url)
-        print "Sending logs to %s" % url
-        r = self.post_multipart(urlparts[1], urlparts[2], fields, files)
-        print r
-        return (r == 'OK')
-
-
-# This script is dual-mode, it can be used as a command line tool and as
-# a library. 
-if sys.argv[0].endswith('logcollect.py') or \
-        sys.argv[0].endswith('logcollect'):
-    print 'log-collect utility 1.0'
-        
-    lc = LogCollect()
-    ls = LogSend()
-
-    logs = ''
-    mode = 'http'
-    
-    if len(sys.argv)==1:
-        print """logcollect.py - send your XO logs to OLPC
-        
-Usage:
-    logcollect.py http  - send logs to default server
-    
-    logcollect.py http://server.name/submit.php
-                         - submit logs to alternative server
-                         
-    logcollect.py file:/media/xxxx-yyyy/mylog.zip
-                         - save the zip file on a USB device or SD card
-                         
-    logcollect.py all file:/media/xxxx-yyyy/mylog.zip
-                        - Save to zip file and include ALL logs
-
-    logcollect.py none http
-                        - Just send info.txt, but no logs via http.
-
-    logcollect.py none file
-                        - Just save info.txt in /dev/shm/logs-SN123.zip
-
-    If you specify 'all' or 'none' you must specify http or file as well.
-        """
-        sys.exit()
-        
-    
-    logbytes = 15360   
-    if len(sys.argv)>1:
-        mode = sys.argv[len(sys.argv)-1]
-        if sys.argv[1] == 'all':
-            logbytes = 0
-        if sys.argv[1] == 'none':
-            logbytes = -1
-   
-
-    if mode.startswith('file'):
-        # file://
-        logs = mode[5:]
- 
-    #if mode.lower().startswith('http'):
-    #    pass
-    #else if mode.lower().startswith('usb'):
-    #    pass
-    #else if mode.lower().startswith('sd'):
-    #    pass
-    
-    logs = lc.write_logs(logs, logbytes)
-    print 'Logs saved in %s' % logs
-    
-    sent_ok = False
-    if len(sys.argv)>1:
-       mode = sys.argv[len(sys.argv)-1]
-    
-    if mode.startswith('http'):
-        print "Trying to send the logs using HTTP (web)"
-        if len(mode) == 4:
-            url = 'http://olpc.scheffers.net/olpc/submit.tcl'
-        else:
-            url = mode
-            
-        if ls.http_post_logs(url, logs):
-            print "Logs were sent."
-            sent_ok = True            
-        else:
-            print "FAILED to send logs."
- 
-
-    if sent_ok:
-        os.remove(logs)
-        print "Logs were sent, tempfile deleted."
-
-
diff --git a/logviewer.py b/logviewer.py
index cdd77ed..2c08a03 100644
--- a/logviewer.py
+++ b/logviewer.py
@@ -16,6 +16,7 @@
 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 import os
+import sys
 import logging
 from gettext import gettext as _
 
@@ -33,9 +34,10 @@ from sugar.graphics.toolbutton import ToolButton
 from sugar.graphics.toggletoolbutton import ToggleToolButton
 from sugar.graphics.palette import Palette
 from sugar.graphics.alert import NotifyAlert
-from logcollect import LogCollect, LogSend
 from sugar.graphics.toolbarbox import ToolbarButton, ToolbarBox
 from sugar.activity.widgets import *
+from jarabe import config
+from jarabe.controlpanel.gui import ControlPanel
 
 # Should be builtin to sugar.graphics.alert.NotifyAlert...
 def _notify_response_cb(notify, response, activity):
@@ -399,10 +401,9 @@ class LogActivity(activity.Activity):
         delete_btn.connect('clicked', self._delete_log_cb)
         edit_toolbar.insert(delete_btn, -1)
 
-        self.collector_palette = CollectorPalette(self)
         collector_btn = ToolButton('zoom-best-fit')
-        collector_btn.set_palette(self.collector_palette)
-        collector_btn.connect('clicked', self._logviewer_cb)
+        collector_btn.set_tooltip(_('Report a problem'))
+        collector_btn.connect('clicked', self._collector_cb)
         edit_toolbar.insert(collector_btn, -1)
 
         edit_toolbar.show_all()
@@ -472,59 +473,10 @@ class LogActivity(activity.Activity):
                 notify.connect('response', _notify_response_cb, self)
                 self.add_alert(notify)
 
-    def _logviewer_cb(self, widget):
-        self.collector_palette.popup(True)
-
-class CollectorPalette(Palette):
-    _DEFAULT_SERVER = 'http://olpc.scheffers.net/olpc/submit.tcl'
-
-    def __init__(self, handler):
-        Palette.__init__(self, _('Log Collector: Send XO information'))
-
-        self._handler = handler
-        
-        self._collector = LogCollect()
+    def _collector_cb(self, widget):
+        # Allows control panel extensions to load.
+        sys.path.append(config.ext_path)
         
-        label = gtk.Label(
-            _('Log collector sends information about the system\n'\
-              'and running processes to a central server.  Use\n'\
-              'this option if you want to report a problem.'))
-        
-        send_button = gtk.Button(_('Send information'))
-        send_button.connect('clicked', self._on_send_button_clicked_cb)
-
-        vbox = gtk.VBox(False, 5)
-        vbox.pack_start(label)
-        vbox.pack_start(send_button)
-        vbox.show_all()
-
-        self.set_content(vbox)
-
-    def _on_send_button_clicked_cb(self, button):
-        success = True
-        try:
-            data = self._collector.write_logs()
-            sender = LogSend()
-            success = sender.http_post_logs(self._DEFAULT_SERVER, data)
-        except:
-            success = False
-
-        os.remove(data)
-        self.popdown(True)
-
-        title = ''
-        msg = ''
-        if success:
-            title = _('Logs sent')
-            msg = _('The logs were uploaded to the server.')
-        else:
-            title = _('Logs not sent')
-            msg = _('The logs could not be uploaded to the server. '\
-                    'Please check your network connection.')
-
-        notify = NotifyAlert()
-        notify.props.title = title
-        notify.props.msg = msg
-        notify.connect('response', _notify_response_cb, self._handler)
-        self._handler.add_alert(notify)
-
+        panel = ControlPanel()
+        panel.set_transient_for(self.get_toplevel())
+        panel.show_section_view('report')
-- 
1.6.2.5

