mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-02-25 18:55:27 -06:00
Show error details if a hotplug operation fails
This commit is contained in:
parent
07992b0736
commit
c6088d650f
@ -1025,15 +1025,20 @@ class vmmAddHardware(gobject.GObject):
|
||||
self.vm.attach_device(self._dev)
|
||||
except Exception, e:
|
||||
logging.debug("Device could not be hotplugged: %s" % str(e))
|
||||
attach_err = True
|
||||
attach_err = (str(e), "".join(traceback.format_exc()))
|
||||
|
||||
if attach_err:
|
||||
if not self.err.yes_no(_("Are you sure you want to add this "
|
||||
"device?"),
|
||||
_("This device could not be attached to "
|
||||
"the running machine. Would you like to "
|
||||
"make the device available after the "
|
||||
"next VM shutdown?")):
|
||||
res = self.err.show_err(
|
||||
_("Are you sure you want to add this device?"),
|
||||
attach_err[0] + "\n\n" + attach_err[1],
|
||||
text2=(
|
||||
_("This device could not be attached to the running machine. "
|
||||
"Would you like to make the device available after the "
|
||||
"next VM shutdown?")),
|
||||
dialog_type=gtk.MESSAGE_WARNING,
|
||||
buttons=gtk.BUTTONS_YES_NO)
|
||||
|
||||
if not res:
|
||||
return (False, None)
|
||||
|
||||
# Alter persistent config
|
||||
|
@ -1597,12 +1597,16 @@ class vmmDetails(gobject.GObject):
|
||||
self.vm.detach_device(dev_id_info)
|
||||
except Exception, e:
|
||||
logging.debug("Device could not be hotUNplugged: %s" % str(e))
|
||||
detach_err = True
|
||||
detach_err = (str(e), "".join(traceback.format_exc()))
|
||||
|
||||
if detach_err:
|
||||
self.err.show_info(
|
||||
_("Device could not be removed from the running machine"),
|
||||
_("This change will take effect after the next VM reboot."))
|
||||
if not detach_err:
|
||||
return
|
||||
|
||||
self.err.show_err(
|
||||
_("Device could not be removed from the running machine"),
|
||||
detach_err[0] + "\n\n" + detach_err[1],
|
||||
text2=_("This change will take effect after the next VM reboot."),
|
||||
dialog_type=gtk.MESSAGE_INFO)
|
||||
|
||||
# Generic config change helpers
|
||||
def _change_config_helper(self,
|
||||
@ -1627,7 +1631,7 @@ class vmmDetails(gobject.GObject):
|
||||
hotplug_funcs = listify(hotplug_funcs)
|
||||
hotplug_funcs_args = listify(hotplug_funcs_args)
|
||||
|
||||
hotplug_err = False
|
||||
hotplug_err = []
|
||||
active = self.vm.is_active()
|
||||
|
||||
# Hotplug change
|
||||
@ -1641,7 +1645,8 @@ class vmmDetails(gobject.GObject):
|
||||
except Exception, e:
|
||||
logging.debug("Hotplug failed: func=%s: %s" % (func,
|
||||
str(e)))
|
||||
hotplug_err = True
|
||||
hotplug_err.append((str(e),
|
||||
"".join(traceback.format_exc())))
|
||||
|
||||
# Persistent config change
|
||||
try:
|
||||
@ -1662,11 +1667,21 @@ class vmmDetails(gobject.GObject):
|
||||
if (hotplug_err or
|
||||
(active and not len(hotplug_funcs) == len(define_funcs))):
|
||||
if len(define_funcs) > 1:
|
||||
self.err.show_info(_("Some changes may require a guest reboot "
|
||||
"to take effect."))
|
||||
msg = _("Some changes may require a guest reboot "
|
||||
"to take effect.")
|
||||
else:
|
||||
self.err.show_info(_("These changes will take effect after "
|
||||
"the next guest reboot."))
|
||||
msg = _("These changes will take effect after "
|
||||
"the next guest reboot.")
|
||||
|
||||
dtype = hotplug_err and gtk.MESSAGE_WARNING or gtk.MESSAGE_INFO
|
||||
hotplug_msg = ""
|
||||
for err1, tb in hotplug_err:
|
||||
hotplug_msg += (err1 + "\n\n" + tb + "\n")
|
||||
|
||||
self.err.show_err(msg, details=hotplug_msg,
|
||||
buttons=gtk.BUTTONS_YES_NO,
|
||||
dialog_type=dtype)
|
||||
|
||||
return True
|
||||
|
||||
########################
|
||||
|
@ -28,107 +28,64 @@ def safe_set_text(self, text):
|
||||
if not util.safe_set_prop(self, "text", text):
|
||||
self.set_markup(text)
|
||||
|
||||
def _launch_dialog(dialog, primary_text, secondary_text, title,
|
||||
sync=True):
|
||||
safe_set_text(dialog, primary_text)
|
||||
dialog.format_secondary_text(secondary_text or None)
|
||||
dialog.set_title(title)
|
||||
|
||||
class vmmErrorDialog (gtk.MessageDialog):
|
||||
res = False
|
||||
if sync:
|
||||
res = dialog.run()
|
||||
res = bool(res in [gtk.RESPONSE_YES, gtk.RESPONSE_OK])
|
||||
dialog.destroy()
|
||||
else:
|
||||
def response_destroy(src, ignore):
|
||||
src.destroy()
|
||||
dialog.connect("response", response_destroy)
|
||||
dialog.show()
|
||||
|
||||
return res
|
||||
|
||||
class vmmErrorDialog (object):
|
||||
def __init__ (self, parent=None):
|
||||
typ = gtk.MESSAGE_ERROR
|
||||
message_format = _("Unexpected Error")
|
||||
message_details = _("An unexpected error occurred")
|
||||
buttons = gtk.BUTTONS_CLOSE
|
||||
default_title = _("Error")
|
||||
flags = 0
|
||||
|
||||
gtk.MessageDialog.__init__ (self,
|
||||
parent, flags, typ, buttons,
|
||||
message_format)
|
||||
|
||||
self.val_err_box = None
|
||||
|
||||
self.message_format = message_format
|
||||
self.message_details = message_details
|
||||
self.buffer = None
|
||||
self.default_title = default_title
|
||||
self.set_title(self.default_title)
|
||||
self.connect("response", self.response_cb)
|
||||
self.connect("delete-event", self.hide_on_delete)
|
||||
|
||||
if not message_details is None:
|
||||
# Expander section with details.
|
||||
expander = gtk.Expander (_("Details"))
|
||||
self.buffer = gtk.TextBuffer ()
|
||||
self.buffer.set_text (self.message_details)
|
||||
sw = gtk.ScrolledWindow ()
|
||||
sw.set_shadow_type (gtk.SHADOW_IN)
|
||||
sw.set_size_request (400, 240)
|
||||
sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
details = gtk.TextView (self.buffer)
|
||||
details.set_editable (False)
|
||||
details.set_overwrite (False)
|
||||
details.set_cursor_visible (False)
|
||||
details.set_wrap_mode (gtk.WRAP_WORD)
|
||||
sw.add (details)
|
||||
details.show ()
|
||||
expander.add (sw)
|
||||
sw.show ()
|
||||
self.vbox.pack_start (expander)
|
||||
expander.show ()
|
||||
self._parent = parent
|
||||
|
||||
def set_parent(self, parent):
|
||||
self.set_transient_for(parent)
|
||||
|
||||
def response_cb(self, src, ignore):
|
||||
src.hide()
|
||||
|
||||
def show_err(self, summary, details, title=None,
|
||||
async=True, debug=True):
|
||||
self.hide()
|
||||
|
||||
if title is None:
|
||||
title = self.default_title
|
||||
self.set_title(title)
|
||||
safe_set_text(self, summary)
|
||||
self.buffer.set_text(details)
|
||||
self._parent = parent
|
||||
def get_parent(self):
|
||||
return self._parent
|
||||
|
||||
def show_err(self, summary, details, title="",
|
||||
async=True, debug=True,
|
||||
dialog_type=gtk.MESSAGE_ERROR,
|
||||
buttons=gtk.BUTTONS_CLOSE,
|
||||
text2=None):
|
||||
if debug:
|
||||
logging.debug("Uncaught Error: %s : %s" % (summary, details))
|
||||
|
||||
if async:
|
||||
self.show()
|
||||
else:
|
||||
self.run()
|
||||
dialog = _errorDialog(parent=self.get_parent(),
|
||||
type=dialog_type, buttons=buttons)
|
||||
|
||||
return dialog.show_dialog(primary_text=summary,
|
||||
secondary_text=text2,
|
||||
details=details, title=title,
|
||||
sync=not async)
|
||||
|
||||
###################################
|
||||
# Simple one shot message dialogs #
|
||||
###################################
|
||||
|
||||
def _simple_dialog(self, dialog_type, buttons, text1,
|
||||
text2, title, async=True):
|
||||
message_box = gtk.MessageDialog(self.get_transient_for(),
|
||||
gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
dialog_type, buttons,
|
||||
text1)
|
||||
if title is not None:
|
||||
message_box.set_title(title)
|
||||
text2, title, async=False):
|
||||
|
||||
if text2 is not None:
|
||||
message_box.format_secondary_text(text2)
|
||||
dialog = gtk.MessageDialog(self.get_parent(),
|
||||
gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
type=dialog_type, buttons=buttons)
|
||||
|
||||
def response_destroy(src, ignore):
|
||||
src.destroy()
|
||||
|
||||
if self.val_err_box:
|
||||
self.val_err_box.destroy()
|
||||
self.val_err_box = message_box
|
||||
|
||||
self.val_err_box.connect("response", response_destroy)
|
||||
res = False
|
||||
if async:
|
||||
self.val_err_box.show()
|
||||
else:
|
||||
res = self.val_err_box.run()
|
||||
res = bool(res in [gtk.RESPONSE_YES, gtk.RESPONSE_OK])
|
||||
|
||||
return res
|
||||
return _launch_dialog(dialog,
|
||||
text1, text2 or "", title or "",
|
||||
sync=not async)
|
||||
|
||||
def val_err(self, text1, text2=None, title=_("Input Error"), async=True):
|
||||
logging.debug("Validation Error: %s" % text1)
|
||||
@ -166,23 +123,42 @@ class vmmErrorDialog (gtk.MessageDialog):
|
||||
def warn_chkbox(self, text1, text2=None, chktext=None, buttons=None):
|
||||
dtype = gtk.MESSAGE_WARNING
|
||||
buttons = buttons or gtk.BUTTONS_OK_CANCEL
|
||||
chkbox = _vmmCheckDialog(self.get_transient_for(), dtype, buttons)
|
||||
return chkbox.show_chkbox(text1, text2, chktext)
|
||||
chkbox = _errorDialog(parent=self.get_parent(),
|
||||
type=dtype,
|
||||
buttons=buttons)
|
||||
return chkbox.show_dialog(primary_text=text1,
|
||||
secondary_text=text2,
|
||||
chktext=chktext)
|
||||
|
||||
def err_chkbox(self, text1, text2=None, chktext=None, buttons=None):
|
||||
dtype = gtk.MESSAGE_ERROR
|
||||
buttons = buttons or gtk.BUTTONS_OK
|
||||
chkbox = _vmmCheckDialog(self.get_transient_for(), dtype, buttons)
|
||||
return chkbox.show_chkbox(text1, text2, chktext)
|
||||
chkbox = _errorDialog(parent=self.get_parent(),
|
||||
type=dtype,
|
||||
buttons=buttons)
|
||||
return chkbox.show_dialog(primary_text=text1,
|
||||
secondary_text=text2,
|
||||
chktext=chktext)
|
||||
|
||||
class _vmmCheckDialog (gtk.MessageDialog):
|
||||
def __init__ (self, parent, typ, buttons):
|
||||
gtk.MessageDialog.__init__ (self, parent, 0, typ, buttons)
|
||||
|
||||
self.connect("response", self.response_cb)
|
||||
self.connect("delete-event", self.hide_on_delete)
|
||||
class _errorDialog (gtk.MessageDialog):
|
||||
"""
|
||||
Custom error dialog with optional check boxes or details drop down
|
||||
"""
|
||||
def __init__ (self, *args, **kwargs):
|
||||
gtk.MessageDialog.__init__ (self, *args, **kwargs)
|
||||
self.set_title("")
|
||||
|
||||
self.chk_vbox = None
|
||||
self.chk_align = None
|
||||
self.init_chkbox()
|
||||
|
||||
self.buffer = None
|
||||
self.buf_expander = None
|
||||
self.init_details()
|
||||
|
||||
def init_chkbox(self):
|
||||
# Init check items
|
||||
self.chk_vbox = gtk.VBox(False, False)
|
||||
self.chk_vbox.set_spacing(0)
|
||||
|
||||
@ -193,28 +169,51 @@ class _vmmCheckDialog (gtk.MessageDialog):
|
||||
self.chk_align.show_all()
|
||||
self.vbox.pack_start(self.chk_align)
|
||||
|
||||
def response_cb(self, src, ignore):
|
||||
src.hide()
|
||||
def init_details(self):
|
||||
# Init details buffer
|
||||
self.buffer = gtk.TextBuffer()
|
||||
self.buf_expander = gtk.Expander (_("Details"))
|
||||
sw = gtk.ScrolledWindow ()
|
||||
sw.set_shadow_type (gtk.SHADOW_IN)
|
||||
sw.set_size_request (400, 240)
|
||||
sw.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
details = gtk.TextView (self.buffer)
|
||||
details.set_editable (False)
|
||||
details.set_overwrite (False)
|
||||
details.set_cursor_visible (False)
|
||||
details.set_wrap_mode (gtk.WRAP_WORD)
|
||||
sw.add(details)
|
||||
self.buf_expander.add(sw)
|
||||
self.vbox.pack_start(self.buf_expander)
|
||||
self.buf_expander.show_all()
|
||||
|
||||
def show_chkbox(self, text1, text2=None, chktext=None):
|
||||
def show_dialog(self, primary_text, secondary_text="",
|
||||
title="", details="", chktext="",
|
||||
sync=True):
|
||||
chkbox = None
|
||||
res = None
|
||||
|
||||
# Hide starting widgets
|
||||
self.hide()
|
||||
self.buf_expander.hide()
|
||||
for c in self.chk_vbox.get_children():
|
||||
self.chk_vbox.remove(c)
|
||||
|
||||
safe_set_text(self, text1)
|
||||
|
||||
if text2:
|
||||
self.format_secondary_text(text2)
|
||||
if details:
|
||||
self.buffer.set_text(details)
|
||||
title = title or _("Error")
|
||||
self.buf_expander.show()
|
||||
|
||||
if chktext:
|
||||
chkbox = gtk.CheckButton(chktext)
|
||||
self.chk_vbox.add(chkbox)
|
||||
chkbox.show()
|
||||
|
||||
res = self.run() in [ gtk.RESPONSE_YES, gtk.RESPONSE_OK ]
|
||||
res = _launch_dialog(self,
|
||||
primary_text, secondary_text or "",
|
||||
title,
|
||||
sync=sync)
|
||||
|
||||
if chktext:
|
||||
res = [res, chkbox.get_active()]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user