virt-manager

changeset 1834:60f80fc312d3

Show error details if a hotplug operation fails
author Cole Robinson <crobinso@redhat.com>
date Wed Dec 01 17:48:07 2010 -0500 (2010-12-01)
parents b205556f54dc
children c2b4365d87d5
files src/virtManager/addhardware.py src/virtManager/details.py src/virtManager/error.py
line diff
     1.1 --- a/src/virtManager/addhardware.py	Wed Dec 01 15:22:38 2010 -0500
     1.2 +++ b/src/virtManager/addhardware.py	Wed Dec 01 17:48:07 2010 -0500
     1.3 @@ -1025,15 +1025,20 @@
     1.4              self.vm.attach_device(self._dev)
     1.5          except Exception, e:
     1.6              logging.debug("Device could not be hotplugged: %s" % str(e))
     1.7 -            attach_err = True
     1.8 +            attach_err = (str(e), "".join(traceback.format_exc()))
     1.9  
    1.10          if attach_err:
    1.11 -            if not self.err.yes_no(_("Are you sure you want to add this "
    1.12 -                                     "device?"),
    1.13 -                                   _("This device could not be attached to "
    1.14 -                                     "the running machine. Would you like to "
    1.15 -                                     "make the device available after the "
    1.16 -                                     "next VM shutdown?")):
    1.17 +            res = self.err.show_err(
    1.18 +                _("Are you sure you want to add this device?"),
    1.19 +                attach_err[0] + "\n\n" + attach_err[1],
    1.20 +                text2=(
    1.21 +                _("This device could not be attached to the running machine. "
    1.22 +                  "Would you like to make the device available after the "
    1.23 +                  "next VM shutdown?")),
    1.24 +                dialog_type=gtk.MESSAGE_WARNING,
    1.25 +                buttons=gtk.BUTTONS_YES_NO)
    1.26 +
    1.27 +            if not res:
    1.28                  return (False, None)
    1.29  
    1.30          # Alter persistent config
     2.1 --- a/src/virtManager/details.py	Wed Dec 01 15:22:38 2010 -0500
     2.2 +++ b/src/virtManager/details.py	Wed Dec 01 17:48:07 2010 -0500
     2.3 @@ -1597,12 +1597,16 @@
     2.4                  self.vm.detach_device(dev_id_info)
     2.5          except Exception, e:
     2.6              logging.debug("Device could not be hotUNplugged: %s" % str(e))
     2.7 -            detach_err = True
     2.8 -
     2.9 -        if detach_err:
    2.10 -            self.err.show_info(
    2.11 -                _("Device could not be removed from the running machine"),
    2.12 -                _("This change will take effect after the next VM reboot."))
    2.13 +            detach_err = (str(e), "".join(traceback.format_exc()))
    2.14 +
    2.15 +        if not detach_err:
    2.16 +            return
    2.17 +
    2.18 +        self.err.show_err(
    2.19 +            _("Device could not be removed from the running machine"),
    2.20 +            detach_err[0] + "\n\n" + detach_err[1],
    2.21 +            text2=_("This change will take effect after the next VM reboot."),
    2.22 +            dialog_type=gtk.MESSAGE_INFO)
    2.23  
    2.24      # Generic config change helpers
    2.25      def _change_config_helper(self,
    2.26 @@ -1627,7 +1631,7 @@
    2.27          hotplug_funcs = listify(hotplug_funcs)
    2.28          hotplug_funcs_args = listify(hotplug_funcs_args)
    2.29  
    2.30 -        hotplug_err = False
    2.31 +        hotplug_err = []
    2.32          active = self.vm.is_active()
    2.33  
    2.34          # Hotplug change
    2.35 @@ -1641,7 +1645,8 @@
    2.36                  except Exception, e:
    2.37                      logging.debug("Hotplug failed: func=%s: %s" % (func,
    2.38                                                                     str(e)))
    2.39 -                    hotplug_err = True
    2.40 +                    hotplug_err.append((str(e),
    2.41 +                                        "".join(traceback.format_exc())))
    2.42  
    2.43          # Persistent config change
    2.44          try:
    2.45 @@ -1662,11 +1667,21 @@
    2.46          if (hotplug_err or
    2.47              (active and not len(hotplug_funcs) == len(define_funcs))):
    2.48              if len(define_funcs) > 1:
    2.49 -                self.err.show_info(_("Some changes may require a guest reboot "
    2.50 -                                     "to take effect."))
    2.51 +                msg = _("Some changes may require a guest reboot "
    2.52 +                        "to take effect.")
    2.53              else:
    2.54 -                self.err.show_info(_("These changes will take effect after "
    2.55 -                                     "the next guest reboot."))
    2.56 +                msg = _("These changes will take effect after "
    2.57 +                        "the next guest reboot.")
    2.58 +
    2.59 +            dtype = hotplug_err and gtk.MESSAGE_WARNING or gtk.MESSAGE_INFO
    2.60 +            hotplug_msg = ""
    2.61 +            for err1, tb in hotplug_err:
    2.62 +                hotplug_msg += (err1 + "\n\n" + tb + "\n")
    2.63 +
    2.64 +            self.err.show_err(msg, details=hotplug_msg,
    2.65 +                              buttons=gtk.BUTTONS_YES_NO,
    2.66 +                              dialog_type=dtype)
    2.67 +
    2.68          return True
    2.69  
    2.70      ########################
     3.1 --- a/src/virtManager/error.py	Wed Dec 01 15:22:38 2010 -0500
     3.2 +++ b/src/virtManager/error.py	Wed Dec 01 17:48:07 2010 -0500
     3.3 @@ -28,107 +28,64 @@
     3.4      if not util.safe_set_prop(self, "text", text):
     3.5          self.set_markup(text)
     3.6  
     3.7 +def _launch_dialog(dialog, primary_text, secondary_text, title,
     3.8 +                   sync=True):
     3.9 +    safe_set_text(dialog, primary_text)
    3.10 +    dialog.format_secondary_text(secondary_text or None)
    3.11 +    dialog.set_title(title)
    3.12  
    3.13 -class vmmErrorDialog (gtk.MessageDialog):
    3.14 +    res = False
    3.15 +    if sync:
    3.16 +        res = dialog.run()
    3.17 +        res = bool(res in [gtk.RESPONSE_YES, gtk.RESPONSE_OK])
    3.18 +        dialog.destroy()
    3.19 +    else:
    3.20 +        def response_destroy(src, ignore):
    3.21 +            src.destroy()
    3.22 +        dialog.connect("response", response_destroy)
    3.23 +        dialog.show()
    3.24 +
    3.25 +    return res
    3.26 +
    3.27 +class vmmErrorDialog (object):
    3.28      def __init__ (self, parent=None):
    3.29 -        typ = gtk.MESSAGE_ERROR
    3.30 -        message_format = _("Unexpected Error")
    3.31 -        message_details = _("An unexpected error occurred")
    3.32 -        buttons = gtk.BUTTONS_CLOSE
    3.33 -        default_title = _("Error")
    3.34 -        flags = 0
    3.35 -
    3.36 -        gtk.MessageDialog.__init__ (self,
    3.37 -                                    parent, flags, typ, buttons,
    3.38 -                                    message_format)
    3.39 -
    3.40 -        self.val_err_box = None
    3.41 -
    3.42 -        self.message_format = message_format
    3.43 -        self.message_details = message_details
    3.44 -        self.buffer = None
    3.45 -        self.default_title = default_title
    3.46 -        self.set_title(self.default_title)
    3.47 -        self.connect("response", self.response_cb)
    3.48 -        self.connect("delete-event", self.hide_on_delete)
    3.49 -
    3.50 -        if not message_details is None:
    3.51 -            # Expander section with details.
    3.52 -            expander = gtk.Expander (_("Details"))
    3.53 -            self.buffer = gtk.TextBuffer ()
    3.54 -            self.buffer.set_text (self.message_details)
    3.55 -            sw = gtk.ScrolledWindow ()
    3.56 -            sw.set_shadow_type (gtk.SHADOW_IN)
    3.57 -            sw.set_size_request (400, 240)
    3.58 -            sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
    3.59 -            details = gtk.TextView (self.buffer)
    3.60 -            details.set_editable (False)
    3.61 -            details.set_overwrite (False)
    3.62 -            details.set_cursor_visible (False)
    3.63 -            details.set_wrap_mode (gtk.WRAP_WORD)
    3.64 -            sw.add (details)
    3.65 -            details.show ()
    3.66 -            expander.add (sw)
    3.67 -            sw.show ()
    3.68 -            self.vbox.pack_start (expander)
    3.69 -            expander.show ()
    3.70 +        self._parent = parent
    3.71  
    3.72      def set_parent(self, parent):
    3.73 -        self.set_transient_for(parent)
    3.74 +        self._parent = parent
    3.75 +    def get_parent(self):
    3.76 +        return self._parent
    3.77  
    3.78 -    def response_cb(self, src, ignore):
    3.79 -        src.hide()
    3.80 -
    3.81 -    def show_err(self, summary, details, title=None,
    3.82 -                 async=True, debug=True):
    3.83 -        self.hide()
    3.84 -
    3.85 -        if title is None:
    3.86 -            title = self.default_title
    3.87 -        self.set_title(title)
    3.88 -        safe_set_text(self, summary)
    3.89 -        self.buffer.set_text(details)
    3.90 -
    3.91 +    def show_err(self, summary, details, title="",
    3.92 +                 async=True, debug=True,
    3.93 +                 dialog_type=gtk.MESSAGE_ERROR,
    3.94 +                 buttons=gtk.BUTTONS_CLOSE,
    3.95 +                 text2=None):
    3.96          if debug:
    3.97              logging.debug("Uncaught Error: %s : %s" % (summary, details))
    3.98  
    3.99 -        if async:
   3.100 -            self.show()
   3.101 -        else:
   3.102 -            self.run()
   3.103 +        dialog = _errorDialog(parent=self.get_parent(),
   3.104 +                              type=dialog_type, buttons=buttons)
   3.105 +
   3.106 +        return dialog.show_dialog(primary_text=summary,
   3.107 +                                  secondary_text=text2,
   3.108 +                                  details=details, title=title,
   3.109 +                                  sync=not async)
   3.110  
   3.111      ###################################
   3.112      # Simple one shot message dialogs #
   3.113      ###################################
   3.114  
   3.115      def _simple_dialog(self, dialog_type, buttons, text1,
   3.116 -                       text2, title, async=True):
   3.117 -        message_box = gtk.MessageDialog(self.get_transient_for(),
   3.118 -                                        gtk.DIALOG_DESTROY_WITH_PARENT,
   3.119 -                                        dialog_type, buttons,
   3.120 -                                        text1)
   3.121 -        if title is not None:
   3.122 -            message_box.set_title(title)
   3.123 +                       text2, title, async=False):
   3.124  
   3.125 -        if text2 is not None:
   3.126 -            message_box.format_secondary_text(text2)
   3.127 +        dialog = gtk.MessageDialog(self.get_parent(),
   3.128 +                                   gtk.DIALOG_DESTROY_WITH_PARENT,
   3.129 +                                   type=dialog_type, buttons=buttons)
   3.130  
   3.131 -        def response_destroy(src, ignore):
   3.132 -            src.destroy()
   3.133 -
   3.134 -        if self.val_err_box:
   3.135 -            self.val_err_box.destroy()
   3.136 -        self.val_err_box = message_box
   3.137 -
   3.138 -        self.val_err_box.connect("response", response_destroy)
   3.139 -        res = False
   3.140 -        if async:
   3.141 -            self.val_err_box.show()
   3.142 -        else:
   3.143 -            res = self.val_err_box.run()
   3.144 -            res = bool(res in [gtk.RESPONSE_YES, gtk.RESPONSE_OK])
   3.145 -
   3.146 -        return res
   3.147 +        return _launch_dialog(dialog,
   3.148 +                              text1, text2 or "", title or "",
   3.149 +                              sync=not async)
   3.150  
   3.151      def val_err(self, text1, text2=None, title=_("Input Error"), async=True):
   3.152          logging.debug("Validation Error: %s" % text1)
   3.153 @@ -166,23 +123,42 @@
   3.154      def warn_chkbox(self, text1, text2=None, chktext=None, buttons=None):
   3.155          dtype = gtk.MESSAGE_WARNING
   3.156          buttons = buttons or gtk.BUTTONS_OK_CANCEL
   3.157 -        chkbox = _vmmCheckDialog(self.get_transient_for(), dtype, buttons)
   3.158 -        return chkbox.show_chkbox(text1, text2, chktext)
   3.159 +        chkbox = _errorDialog(parent=self.get_parent(),
   3.160 +                              type=dtype,
   3.161 +                              buttons=buttons)
   3.162 +        return chkbox.show_dialog(primary_text=text1,
   3.163 +                                  secondary_text=text2,
   3.164 +                                  chktext=chktext)
   3.165  
   3.166      def err_chkbox(self, text1, text2=None, chktext=None, buttons=None):
   3.167          dtype = gtk.MESSAGE_ERROR
   3.168          buttons = buttons or gtk.BUTTONS_OK
   3.169 -        chkbox = _vmmCheckDialog(self.get_transient_for(), dtype, buttons)
   3.170 -        return chkbox.show_chkbox(text1, text2, chktext)
   3.171 +        chkbox = _errorDialog(parent=self.get_parent(),
   3.172 +                              type=dtype,
   3.173 +                              buttons=buttons)
   3.174 +        return chkbox.show_dialog(primary_text=text1,
   3.175 +                                  secondary_text=text2,
   3.176 +                                  chktext=chktext)
   3.177  
   3.178 -class _vmmCheckDialog (gtk.MessageDialog):
   3.179 -    def __init__ (self, parent, typ, buttons):
   3.180 -        gtk.MessageDialog.__init__ (self, parent, 0, typ, buttons)
   3.181  
   3.182 -        self.connect("response", self.response_cb)
   3.183 -        self.connect("delete-event", self.hide_on_delete)
   3.184 +class _errorDialog (gtk.MessageDialog):
   3.185 +    """
   3.186 +    Custom error dialog with optional check boxes or details drop down
   3.187 +    """
   3.188 +    def __init__ (self, *args, **kwargs):
   3.189 +        gtk.MessageDialog.__init__ (self, *args, **kwargs)
   3.190          self.set_title("")
   3.191  
   3.192 +        self.chk_vbox = None
   3.193 +        self.chk_align = None
   3.194 +        self.init_chkbox()
   3.195 +
   3.196 +        self.buffer = None
   3.197 +        self.buf_expander = None
   3.198 +        self.init_details()
   3.199 +
   3.200 +    def init_chkbox(self):
   3.201 +        # Init check items
   3.202          self.chk_vbox = gtk.VBox(False, False)
   3.203          self.chk_vbox.set_spacing(0)
   3.204  
   3.205 @@ -193,28 +169,51 @@
   3.206          self.chk_align.show_all()
   3.207          self.vbox.pack_start(self.chk_align)
   3.208  
   3.209 -    def response_cb(self, src, ignore):
   3.210 -        src.hide()
   3.211 +    def init_details(self):
   3.212 +        # Init details buffer
   3.213 +        self.buffer = gtk.TextBuffer()
   3.214 +        self.buf_expander = gtk.Expander (_("Details"))
   3.215 +        sw = gtk.ScrolledWindow ()
   3.216 +        sw.set_shadow_type (gtk.SHADOW_IN)
   3.217 +        sw.set_size_request (400, 240)
   3.218 +        sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
   3.219 +        details = gtk.TextView (self.buffer)
   3.220 +        details.set_editable (False)
   3.221 +        details.set_overwrite (False)
   3.222 +        details.set_cursor_visible (False)
   3.223 +        details.set_wrap_mode (gtk.WRAP_WORD)
   3.224 +        sw.add(details)
   3.225 +        self.buf_expander.add(sw)
   3.226 +        self.vbox.pack_start(self.buf_expander)
   3.227 +        self.buf_expander.show_all()
   3.228  
   3.229 -    def show_chkbox(self, text1, text2=None, chktext=None):
   3.230 +    def show_dialog(self, primary_text, secondary_text="",
   3.231 +                    title="", details="", chktext="",
   3.232 +                    sync=True):
   3.233          chkbox = None
   3.234          res = None
   3.235  
   3.236 +        # Hide starting widgets
   3.237          self.hide()
   3.238 +        self.buf_expander.hide()
   3.239          for c in self.chk_vbox.get_children():
   3.240              self.chk_vbox.remove(c)
   3.241  
   3.242 -        safe_set_text(self, text1)
   3.243 -
   3.244 -        if text2:
   3.245 -            self.format_secondary_text(text2)
   3.246 +        if details:
   3.247 +            self.buffer.set_text(details)
   3.248 +            title = title or _("Error")
   3.249 +            self.buf_expander.show()
   3.250  
   3.251          if chktext:
   3.252              chkbox = gtk.CheckButton(chktext)
   3.253              self.chk_vbox.add(chkbox)
   3.254              chkbox.show()
   3.255  
   3.256 -        res = self.run() in [ gtk.RESPONSE_YES, gtk.RESPONSE_OK ]
   3.257 +        res = _launch_dialog(self,
   3.258 +                             primary_text, secondary_text or "",
   3.259 +                             title,
   3.260 +                             sync=sync)
   3.261 +
   3.262          if chktext:
   3.263              res = [res, chkbox.get_active()]
   3.264