Re: [PATCH] Add support for filing tracebacks directly into bugzilla.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Chris Lumens wrote:
This patch adds support for save to bugzilla, using the python-bugzilla module.
We get the bugzilla URL from product.bugUrl and require the user to already
have a valid account with that bugzilla instance.  That should cut down on
potential abuse.

To cut down on the number of possible duplicates, we hash the file name,
function name, and line of each frame in the traceback and store that hash in
the bug itself.  Before filing a new bug, we query for any bugs containing that
hash value.  If found, we simply add the traceback as a new attachment and put
the user on the CC list.  If not found, we create a new bug.  Either way, the
user is encouraged to visit their bug and make a more meaningful comment.
---
 exception.py     |  136 +++++++++++++++++++++++++++++++++++++++++++-----------
 gui.py           |    6 ++-
 text.py          |   21 ++++++---
 ui/exnSave.glade |   52 +++++++++++++++++++--
 4 files changed, 175 insertions(+), 40 deletions(-)

diff --git a/exception.py b/exception.py
index ab01ecd..4572eef 100644
--- a/exception.py
+++ b/exception.py
@@ -51,6 +51,8 @@ class AnacondaExceptionDump:
         self.value = value
         self.tb = tb
+ self.tbFile = None
+
         self._dumpHash = {}
# Reverse the order that tracebacks are printed so people will hopefully quit
@@ -219,6 +221,12 @@ class AnacondaExceptionDump:
return hashlib.sha256(s).hexdigest() + def write(self, anaconda):
+        self.tbFile = "/tmp/anacdump.txt"
+        fd = open(self.tbFile, "w")
+        self.dump(fd, anaconda)
+        fd.close()
+
 # Save the traceback to a removable storage device, such as a floppy disk
 # or a usb/firewire drive.  If there's no filesystem on the disk/partition,
 # write a vfat one.
@@ -273,8 +281,99 @@ def copyExceptionToDisk(anaconda, device):
     isys.umount("/tmp/crash")
     return True
-def runSaveDialog(anaconda, longTracebackFile):
-    saveWin = anaconda.intf.saveExceptionWindow(anaconda, longTracebackFile)
+def saveToBugzilla(anaconda, exn, dest):
+    import bugzilla, xmlrpclib
+    import product, rpmUtils
+
+    if dest[0].strip() == "" or dest[1].strip() == "" or dest[2].strip() == "":
+        anaconda.intf.messageWindow(_("Invalid Bug Information"),
+                                    _("Please provide a valid username, "
+                                      "password, and short bug description."))
+        return False
+
+    hash = exn.hash()
+
+    if product.bugUrl.startswith("http://";):
+        bugUrl = "https://"; + product.bugUrl[7:]
+    elif product.bugUrl.startswith("https://";):
+        bugUrl = product.bugUrl
+    else:
+        anaconda.intf.messageWindow(_("No bugzilla URL"),
+                                    _("Your distribution does not provide a "
+                                      "bug reporting URL, so you cannot save "
+                                      "your exception to a remote bug tracking "
+                                      "system."))
+        return False
+
+    if not exn.tbFile:
+        exn.write(anaconda)
+
+    bz = bugzilla.Bugzilla(url = "%s/xmlrpc.cgi" % bugUrl)
+
+    if not bz.login(dest[0], dest[1]):
+        anaconda.intf.messageWindow(_("Unable To Login"),
+                                    _("There was an error logging into %s "
+                                      "using the provided username and "
+                                      "password.") % product.bugUrl)
+        return False
+
+    # Are there any existing bugs with this hash value?  If so we will just
+    # add this traceback to the bug report and put the reporter on the CC
+    # list.  Otherwise, we need to create a new bug.
+    try:
+        buglist = bz.query({'status_whiteboard': hash})
+    except xmlrpclib.ProtocolError, e:
+        anaconda.intf.messageWindow(_("Unable To File Bug"),
+                                    _("Your bug could not be filed due to the "
+                                      "following error when communicating with "
+                                      "bugzilla:\n\n%s" % str(e)))
+        return False
+
+    # FIXME:  need to handle all kinds of errors here
+    if len(buglist) == 0:
+        bug = bz.createbug(product=product.productName,
+                           component="anaconda",
+                           version=product.productVersion,
+                           rep_platform=rpmUtils.arch.getBaseArch(),
+                           bug_severity="medium",
+                           priority="medium",
+                           op_sys="Linux",
+                           bug_file_loc="http://";,
+                           short_desc=dest[2],
+                           comment="This bug was filed automatically by anaconda.")
+        bug.setwhiteboard("anaconda_trace_hash:%s" % hash, which="status")
+        bz.attachfile(bug.bug_id, exn.tbFile, "Attached traceback automatically from anaconda.",
+                      contenttype="text/plain")
+
+        # Tell the user we created a new bug for them and that they should
+        # go add a descriptive comment.
+        anaconda.intf.messageWindow(_("Bug Created"),
+            _("A new bug has been created with your traceback attached. "
+              "Please add additional information such as what you were doing "
+              "when you encountered the bug, screenshots, and whatever else "
+              "is appropriate to the following bug:\n\n%s/%s") % (bugUrl, bug.bug_id),
+            type="custom", custom_icon="info",
+            custom_buttons=[_("_Exit installer")])
+        sys.exit(0)
+    else:
+        id = buglist[0].bug_id
+        bz.attachfile(id, exn.tbFile, "Attached traceback automatically from anaconda.",
+                      contenttype="text/plain")
+        bz._updatecc(id, [dest[0]], "add")
+

Looking at this I can see the possibility of having a bug with a lot of comments in it.  Like the Samsung bug that everybody hit.  I'm not for changing it, I'm just saying that it might be something to consider if we are getting bugs with an unmanageable list of tracebacks.

+        # Tell the user which bug they've been CC'd on and that they should
+        # go add a descriptive comment.
+        anaconda.intf.messageWindow(_("Bug Updated"),
+            _("A bug with your information already exists.  Your account has "
+              "been added to the CC list and your traceback added as a "
+              "comment.  Please add additional descriptive information to the "
+              "following bug:\n\n%s/%s") % (bugUrl, id),
+            type="custom", custom_icon="info",
+            custom_buttons=[_("_Exit installer")])
+        sys.exit(0)
+
+def runSaveDialog(anaconda, exn):
+    saveWin = anaconda.intf.saveExceptionWindow(anaconda, exn.tbFile)
     if not saveWin:
         anaconda.intf.__del__()
         os.kill(os.getpid(), signal.SIGKILL)
@@ -303,7 +402,7 @@ def runSaveDialog(anaconda, longTracebackFile):
             elif saveWin.saveToLocal():
                 dest = saveWin.getDest()
                 try:
-                    shutil.copyfile("/tmp/anacdump.txt", "%s/InstallError.txt" %(dest,))
+                    shutil.copyfile(exn.tbFile, "%s/InstallError.txt" %(dest,))
                     anaconda.intf.messageWindow(_("Dump Written"),
                         _("Your system's state has been successfully written to "
                           "the disk. The installer will now exit."),
@@ -311,30 +410,14 @@ def runSaveDialog(anaconda, longTracebackFile):
                         custom_buttons=[_("_Exit installer")])
                     sys.exit(0)
                 except Exception, e:
-                    log.error("Failed to copy anacdump.txt to %s/anacdump.txt: %s" %(dest, e))
+                    log.error("Failed to copy %s to %s/anacdump.txt: %s" %(exn.tbFile, dest, e))
                 else:
                     anaconda.intf.messageWindow(_("Dump Not Written"),
                         _("There was a problem writing the system state to the "
                           "disk."))
                     continue
             else:
-                if not hasActiveNetDev() and not anaconda.intf.enableNetwork(anaconda):
-                    scpSucceeded = False
-                else:
-                    scpInfo = saveWin.getDest()
-                    scpSucceeded = copyExceptionToRemote(anaconda.intf, scpInfo)
-
-                if scpSucceeded:
-                    anaconda.intf.messageWindow(_("Dump Written"),
-                        _("Your system's state has been successfully written to "
-                          "the remote host.  The installer will now exit."),
-                        type="custom", custom_icon="info",
-                        custom_buttons=[_("_Exit installer")])
-                    sys.exit(0)
-                else:
-                    anaconda.intf.messageWindow(_("Dump Not Written"),
-                        _("There was a problem writing the system state to the "
-                          "remote host."))
+                if not saveToBugzilla(anaconda, exn, saveWin.getDest()):
                     continue
         elif rc == EXN_CANCEL:
             break
@@ -348,14 +431,11 @@ def handleException(anaconda, (type, value, tb)):
     # restore original exception handler
     sys.excepthook = sys.__excepthook__
+ # Save the exception file to local storage first.
     exn = AnacondaExceptionDump(type, value, tb)
+    exn.write(anaconda)
     text = str(exn)
- # save to local storage first
-    out = open("/tmp/anacdump.txt", "w")
-    exn.dump(out, anaconda)
-    out.close()
-
     # see if /mnt/sysimage is present and put exception there as well
     if os.access("/mnt/sysimage/root", os.X_OK):
         try:
@@ -371,7 +451,7 @@ def handleException(anaconda, (type, value, tb)):
     except:
         pass
- mainWin = anaconda.intf.mainExceptionWindow(text, "/tmp/anacdump.txt")
+    mainWin = anaconda.intf.mainExceptionWindow(text, exn.tbFile)
     if not mainWin:
         anaconda.intf.__del__()
         os.kill(os.getpid(), signal.SIGKILL)
@@ -415,4 +495,4 @@ def handleException(anaconda, (type, value, tb)):
             pdb.post_mortem (tb)
             os.kill(os.getpid(), signal.SIGKILL)
         elif rc == EXN_SAVE:
-            runSaveDialog(anaconda, "/tmp/anacdump.txt")
+            runSaveDialog(anaconda, exn)
diff --git a/gui.py b/gui.py
index 2a85722..2629576 100755
--- a/gui.py
+++ b/gui.py
@@ -732,6 +732,7 @@ class SaveExceptionWindow:
self.usernameEntry = exnxml.get_widget("usernameEntry")
         self.passwordEntry = exnxml.get_widget("passwordEntry")
+        self.bugDesc = exnxml.get_widget("bugDesc")
self.diskButton = exnxml.get_widget("diskButton")
         self.diskCombo = exnxml.get_widget("diskCombo")
@@ -745,6 +746,8 @@ class SaveExceptionWindow:
         self.remoteButton.connect("toggled", self.radio_changed)
         self.localButton.connect("toggled", self.radio_changed)
+ self.remoteButton.set_label(self.remoteButton.get_label() % product.bugUrl)
+
         cell = gtk.CellRendererText()
         self.diskCombo.pack_start(cell, True)
         self.diskCombo.set_attributes(cell, text=1)
@@ -795,7 +798,8 @@ class SaveExceptionWindow:
         elif self.saveToLocal():
             return self.localChooser.get_filename()
         else:
-            return map(lambda e: e.get_text(), [self.usernameEntry, self.passwordEntry])
+            return map(lambda e: e.get_text(), [self.usernameEntry, self.passwordEntry,
+                                                self.bugDesc])
def pop(self):
         self.window.destroy()
diff --git a/text.py b/text.py
index 41ca1c7..069b747 100644
--- a/text.py
+++ b/text.py
@@ -29,6 +29,7 @@ import iutil
 import time
 import signal
 import parted
+import product
 import string
 from language import expandLangs
 from flags import flags
@@ -157,9 +158,11 @@ class SaveExceptionWindow:
         if self.rg.getSelection() == "disk":
             self.usernameEntry.setFlags(FLAG_DISABLED, FLAGS_SET)
             self.passwordEntry.setFlags(FLAG_DISABLED, FLAGS_SET)
+            self.bugDesc.setFlags(FLAG_DISABLED, FLAGS_SET)
         else:
             self.usernameEntry.setFlags(FLAG_DISABLED, FLAGS_RESET)
             self.passwordEntry.setFlags(FLAG_DISABLED, FLAGS_RESET)
+            self.bugDesc.setFlags(FLAG_DISABLED, FLAGS_RESET)
def getrc(self):
         if self.rc == TEXT_OK_CHECK:
@@ -171,7 +174,8 @@ class SaveExceptionWindow:
         if self.saveToDisk():
             return self.diskList.current()
         else:
-            return map(lambda e: e.value(), [self.usernameEntry, self.passwordEntry])
+            return map(lambda e: e.value(), [self.usernameEntry, self.passwordEntry,
+                                             self.bugDesc])
def pop(self):
         self.screen.popWindow()
@@ -182,7 +186,7 @@ class SaveExceptionWindow:
self.rg = RadioGroup()
         self.diskButton = self.rg.add(_("Save to Disk"), "disk", True)
-        self.remoteButton = self.rg.add(_("Save to Remote"), "remote", False)
+        self.remoteButton = self.rg.add(_("Save to Remote (%s)") % product.bugUrl, "remote", False)
self.diskButton.setCallback(self._destCb, None)
         self.remoteButton.setCallback(self._destCb, None)
@@ -190,14 +194,17 @@ class SaveExceptionWindow:
         buttons = ButtonBar(self.screen, [TEXT_OK_BUTTON, TEXT_CANCEL_BUTTON])
         self.usernameEntry = Entry(24)
         self.passwordEntry = Entry(24, password=1)
+        self.bugDesc = Entry(24)
self.diskList = Listbox(height=3, scroll=1) - remoteGrid = Grid(2, 2)
-        remoteGrid.setField(Label(_("User name")), 0, 2, anchorLeft=1)
-        remoteGrid.setField(self.usernameEntry, 1, 2)
-        remoteGrid.setField(Label(_("Password")), 0, 3, anchorLeft=1)
-        remoteGrid.setField(self.passwordEntry, 1, 3)
+        remoteGrid = Grid(2, 3)
+        remoteGrid.setField(Label(_("User name")), 0, 0, anchorLeft=1)
+        remoteGrid.setField(self.usernameEntry, 1, 0)
+        remoteGrid.setField(Label(_("Password")), 0, 1, anchorLeft=1)
+        remoteGrid.setField(self.passwordEntry, 1, 1)
+        remoteGrid.setField(Label(_("Bug Description")), 0, 2, anchorLeft=1)
+        remoteGrid.setField(self.bugDesc, 1, 2)
toplevel.add(self.diskButton, 0, 0, (0, 0, 0, 1))
         toplevel.add(self.diskList, 0, 1, (0, 0, 0, 1))
diff --git a/ui/exnSave.glade b/ui/exnSave.glade
index 0bab909..6c22e2f 100644
--- a/ui/exnSave.glade
+++ b/ui/exnSave.glade
@@ -273,8 +273,8 @@
 			  <child>
 			    <widget class="GtkLabel" id="label4">
 			      <property name="visible">True</property>
-			      <property name="label">User name</property>
-			      <property name="use_underline">False</property>
+			      <property name="label">_User name</property>
+			      <property name="use_underline">True</property>
 			      <property name="use_markup">False</property>
 			      <property name="justify">GTK_JUSTIFY_LEFT</property>
 			      <property name="wrap">False</property>
@@ -298,8 +298,8 @@
 			  <child>
 			    <widget class="GtkLabel" id="label5">
 			      <property name="visible">True</property>
-			      <property name="label">Password</property>
-			      <property name="use_underline">False</property>
+			      <property name="label">_Password</property>
+			      <property name="use_underline">True</property>
 			      <property name="use_markup">False</property>
 			      <property name="justify">GTK_JUSTIFY_LEFT</property>
 			      <property name="wrap">False</property>
@@ -319,6 +319,31 @@
 			      <property name="fill">True</property>
 			    </packing>
 			  </child>
+
+			  <child>
+			    <widget class="GtkLabel" id="label7">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">_Bug description</property>
+			      <property name="use_underline">True</property>
+			      <property name="use_markup">False</property>
+			      <property name="justify">GTK_JUSTIFY_LEFT</property>
+			      <property name="wrap">False</property>
+			      <property name="selectable">False</property>
+			      <property name="xalign">0.5</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xpad">0</property>
+			      <property name="ypad">0</property>
+			      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			      <property name="width_chars">-1</property>
+			      <property name="single_line_mode">False</property>
+			      <property name="angle">0</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
 			</widget>
 			<packing>
 			  <property name="padding">0</property>
@@ -371,6 +396,25 @@
 			      <property name="fill">True</property>
 			    </packing>
 			  </child>
+
+			  <child>
+			    <widget class="GtkEntry" id="bugDesc">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="editable">True</property>
+			      <property name="visibility">True</property>
+			      <property name="max_length">0</property>
+			      <property name="text" translatable="yes"></property>
+			      <property name="has_frame">True</property>
+			      <property name="invisible_char">•</property>
+			      <property name="activates_default">False</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
 			</widget>
 			<packing>
 			  <property name="padding">0</property>

--
Joel Andres Granados
Red Hat / Brno, Czech Republic

_______________________________________________
Anaconda-devel-list mailing list
Anaconda-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/anaconda-devel-list

[Index of Archives]     [Kickstart]     [Fedora Users]     [Fedora Legacy List]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [Yosemite Photos]     [KDE Users]     [Fedora Tools]
  Powered by Linux