[virt-tools-list] [virt-manager][PATCH v2 2/2] Add delete VM option in console viewer.

Cole Robinson crobinso at redhat.com
Tue Jun 11 15:03:13 UTC 2013


On 06/10/2013 05:29 PM, Leonardo Augusto Guimarães Garcia wrote:
> Thanks for the review! :)
> 
> Comments below.
> 
> On 06/08/2013 08:30 PM, Cole Robinson wrote:
>> Thanks for the patches! General idea is fine, some comments below.
>>
>> On 06/05/2013 03:40 PM, lagarcia at linux.vnet.ibm.com wrote:
>>> From: Leonardo Garcia <lagarcia at br.ibm.com>
>>>
>>> ---
>>>   ui/vmm-details.ui        |   15 +++++++++++++++
>>>   virtManager/baseclass.py |    5 +++--
>>>   virtManager/details.py   |   11 +++++++++--
>>>   virtManager/engine.py    |   46
>>> +++++++++++++++++++++++++++++++++++++++++++++-
>>>   4 files changed, 72 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/ui/vmm-details.ui b/ui/vmm-details.ui
>>> index ddb2a71..ea4b53e 100644
>>> --- a/ui/vmm-details.ui
>>> +++ b/ui/vmm-details.ui
>>> @@ -314,6 +314,21 @@
>>>                         </object>
>>>                       </child>
>>>                       <child>
>>> +                      <object class="GtkMenuItem" id="details-menu-delete">
>>> +                        <property name="visible">True</property>
>>> +                        <property name="can_focus">False</property>
>>> +                        <property name="label"
>>> translatable="yes">_Delete...</property>
>>> +                        <property name="use_underline">True</property>
>>> +                        <signal name="activate"
>>> handler="on_details_menu_delete_activate" swapped="no"/>
>>> +                      </object>
>>> +                    </child>
>>> +                    <child>
>>> +                      <object class="GtkSeparatorMenuItem" id="separator2">
>>> +                        <property name="visible">True</property>
>>> +                        <property name="can_focus">False</property>
>>> +                      </object>
>>> +                    </child>
>>> +                    <child>
>>>                         <object class="GtkMenuItem"
>>> id="details-menu-vm-screenshot">
>>>                           <property name="visible">True</property>
>>>                           <property name="can_focus">False</property>
>>> diff --git a/virtManager/baseclass.py b/virtManager/baseclass.py
>>> index 7bc7812..c3a093e 100644
>>> --- a/virtManager/baseclass.py
>>> +++ b/virtManager/baseclass.py
>>> @@ -194,8 +194,9 @@ class vmmGObjectUI(vmmGObject):
>>>           self.close()
>>>           vmmGObject.cleanup(self)
>>>           self.builder = None
>>> -        self.topwin.destroy()
>>> -        self.topwin = None
>>> +        if self.topwin:
>>> +            self.topwin.destroy()
>>> +            self.topwin = None
>>>           self.uifile = None
>>>           self.err = None
>>>   
>> Hmm, I can see from the vmmGObjectUI code why this is needed but it shouldn't
>> be required in practice. I've pushed a cleanup now that should make this
>> unnecessary, so please drop this bit.
> I thought these pieces you commented about would require some more explanation.
> 
> With the changes made in the --show* command line option, I am usually running
> virt-manager with only one window opened: the details window. If I close this
> window, the following sequence of events will happen:
> 
> user action: close window -> call: vmmDetails.close -> emit: details-closed ->
> call: vmmEngine.decrement_window_counter --- as it is the last window --->
> call: vmmEngine.exit_app -> call: vmmEngine._cleanup -> call:
> vmmEngine.cleanup_conn -> call: vmmDetails.cleanup (definition in base class)
> -> call: vmmDetails.close
> 
> I think it is easier to see this through the stacktrace:
> 
>   File "./virt-manager", line 306, in <module>
>     main()
>   File "./virt-manager", line 301, in main
>     engine.application.run(None)
>   File "/usr/lib64/python2.7/site-packages/gi/types.py", line 47, in function
>     return info.invoke(*args, **kwargs)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/details.py", line
> 590, in close
>     return self._close()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/details.py", line
> 615, in _close
>     self.emit("details-closed")
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 157, in emit
>     return GObject.GObject.emit(self, signal_name, *args)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 336,
> in decrement_window_counter
>     self.exit_app(src)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 393,
> in exit_app
>     self.cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 66, in cleanup
>     self._cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 385,
> in _cleanup
>     self.cleanup_conn(uri)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 474,
> in cleanup_conn
>     win.cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 194, in cleanup
>     self.close()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/details.py", line
> 590, in close
>     return self._close()
> 
> This sequence works fine as, eventhough vmmDetails.close is reentrant,
> vmmDetails.cleanup is called only once.
> 
> However, when we have only the details window opened and we choose to delete
> the VM (the purpose of this patch proposal), something different happens:
> 
> emit: vm-removed -> call: vmmEngine._do_vm_removed --- tries to cleanup the
> details window corresponding to the deleted VM ---> call vmmDetails.cleanup
> (definition in base class) -> call: vmmDetails.close -> emit: details-closed
> -> call: vmmEngine.decrement_window_counter --- as it is the last window --->
> call: vmmEngine.exit_app -> call: vmmEngine._cleanup -> call:
> vmmEngine.cleanup_conn -> call: vmmDetails.cleanup (definition in base class)
> -> call: vmmDetails.close
> 
> Or, in the stack trace:
> 
>   File "./virt-manager", line 306, in <module>
>     main()
>   File "./virt-manager", line 301, in main
>     engine.application.run(None)
>   File "/usr/lib64/python2.7/site-packages/gi/types.py", line 47, in function
>     return info.invoke(*args, **kwargs)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/connection.py", line
> 1330, in tick_send_signals
>     self.emit("vm-removed", uuid)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 157, in emit
>     return GObject.GObject.emit(self, signal_name, *args)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 243,
> in _do_vm_removed
>     self.conns[hvuri]["windowDetails"][vmuuid].cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 194, in cleanup
>     self.close()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/details.py", line
> 590, in close
>     return self._close()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/details.py", line
> 615, in _close
>     self.emit("details-closed")
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 157, in emit
>     return GObject.GObject.emit(self, signal_name, *args)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 336,
> in decrement_window_counter
>     self.exit_app(src)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 393,
> in exit_app
>     self.cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 66, in cleanup
>     self._cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 385,
> in _cleanup
>     self.cleanup_conn(uri)
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 474,
> in cleanup_conn
>     win.cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 194, in cleanup
>     self.close()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/details.py", line
> 590, in close
>     return self._close()
> 
> The last call to vmmDetails.cleanup will destroy self.topwin. However, when
> the processing goes back to the first call to vmmDetails.cleanup, we will get
> an exception, as self.topwin has already been destroyed:
> 
> 2013-06-10 16:56:55,719 (delete:74): Showing delete wizard
> 2013-06-10 16:57:00,644 (asyncjob:193): Creating async job for function
> cb=<bound method vmmDeleteDialog._async_delete of <vmmDeleteDialog object at
> 0x2fd1730 (virtManager+delete+vmmDeleteDialog at 0x321cf00)>>
> 2013-06-10 16:57:00,754 (delete:173): Threading off connection to delete vol.
> 2013-06-10 16:57:00,756 (delete:179): Deleting path:
> /var/lib/libvirt/images/Fedora18-test-clone.img
> 2013-06-10 16:57:00,927 (delete:187): Removing VM 'Fedora18-test-clone'
> 2013-06-10 16:57:02,326 (delete:83): Closing delete wizard
> 2013-06-10 16:57:02,600 (details:589): Closing VM details: <vmmDomain object
> at 0x235c870 (virtManager+domain+vmmDomain at 0x7f5894005240)>
> 2013-06-10 16:57:02,603 (engine:330): window counter decremented to 0
> 2013-06-10 16:57:02,618 (manager:211): Closing manager
> 2013-06-10 16:57:02,627 (delete:83): Closing delete wizard
> 2013-06-10 16:57:02,633 (details:589): Closing VM details: <vmmDomain object
> at 0x235c870 (virtManager+domain+vmmDomain at 0x7f5894005240)>
> 2013-06-10 16:57:02,781 (engine:408): Exiting app normally.
> 2013-06-10 16:57:02,782 (baseclass:68): Error cleaning up <vmmDetails object
> at 0x2514f50 (virtManager+details+vmmDetails at 0x2aa2320)>
> Traceback (most recent call last):
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 66, in cleanup
>     self._cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/details.py", line
> 564, in _cleanup
>     self.console.cleanup()
> AttributeError: 'NoneType' object has no attribute 'cleanup'
> 2013-06-10 16:57:02,785 (cliutils:87): Uncaught exception:
> Traceback (most recent call last):
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 243,
> in _do_vm_removed
>     self.conns[hvuri]["windowDetails"][vmuuid].cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 197, in cleanup
>     self.topwin.destroy()
> AttributeError: 'NoneType' object has no attribute 'destroy'
> 
> Traceback (most recent call last):
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 243,
> in _do_vm_removed
>     self.conns[hvuri]["windowDetails"][vmuuid].cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 197, in cleanup
>     self.topwin.destroy()
> AttributeError: 'NoneType' object has no attribute 'destroy'
> 
> The changes you made in vmmGObjectUI fixed this issue, but didn't fix the
> other two issues below.
>>
>>> diff --git a/virtManager/details.py b/virtManager/details.py
>>> index 8e927b1..1323232 100644
>>> --- a/virtManager/details.py
>>> +++ b/virtManager/details.py
>>> @@ -329,6 +329,7 @@ class vmmDetails(vmmGObjectUI):
>>>           "action-exit-app": (GObject.SignalFlags.RUN_FIRST, None, []),
>>>           "action-view-manager": (GObject.SignalFlags.RUN_FIRST, None, []),
>>>           "action-migrate-domain": (GObject.SignalFlags.RUN_FIRST, None,
>>> [str, str]),
>>> +        "action-delete-domain": (GObject.SignalFlags.RUN_FIRST, None,
>>> [str, str]),
>>>           "action-clone-domain": (GObject.SignalFlags.RUN_FIRST, None,
>>> [str, str]),
>>>           "details-closed": (GObject.SignalFlags.RUN_FIRST, None, []),
>>>           "details-opened": (GObject.SignalFlags.RUN_FIRST, None, []),
>>> @@ -412,6 +413,7 @@ class vmmDetails(vmmGObjectUI):
>>>               "on_details_menu_pause_activate": self.control_vm_pause,
>>>               "on_details_menu_clone_activate": self.control_vm_clone,
>>>               "on_details_menu_migrate_activate": self.control_vm_migrate,
>>> +            "on_details_menu_delete_activate": self.control_vm_delete,
>>>               "on_details_menu_screenshot_activate":
>>> self.control_vm_screenshot,
>>>               "on_details_menu_view_toolbar_activate": self.toggle_toolbar,
>>>               "on_details_menu_view_manager_activate": self.view_manager,
>>> @@ -559,8 +561,9 @@ class vmmDetails(vmmGObjectUI):
>>>           for serial in self.serial_tabs:
>>>               self._close_serial_tab(serial)
>>>   -        self.console.cleanup()
>>> -        self.console = None
>>> +        if self.console:
>>> +            self.console.cleanup()
>>> +            self.console = None
>>>
>> self.console should be unconditionally set, so I'm not sure why this is needed.
> If the previous patch (mine or yours) is applied to baseclass.py, we will hit
> another error:
> 
> 2013-06-10 17:51:59,007 (engine:408): Exiting app normally.
> 2013-06-10 17:51:59,008 (baseclass:68): Error cleaning up <vmmDetails object
> at 0x330fe60 (virtManager+details+vmmDetails at 0x7f2218004620)>
> Traceback (most recent call last):
>   File "/home/laggarcia/src/git/virt-manager/virtManager/baseclass.py", line
> 66, in cleanup
>     self._cleanup()
>   File "/home/laggarcia/src/git/virt-manager/virtManager/details.py", line
> 564, in _cleanup
>     self.console.cleanup()
> AttributeError: 'NoneType' object has no attribute 'cleanup'
> 
> The cause for this error is the same as the previous one: vmmDetails cleanup
> will call vmmDetails._cleanup at some point. The last call to this function
> will correctly execute self.console.cleanup(). However, the first call will
> generate above error. Hence, the need for this check.
>>
>>>           self.vm = None
>>>           self.conn = None
>>> @@ -1580,6 +1583,10 @@ class vmmDetails(vmmGObjectUI):
>>>           self.emit("action-migrate-domain",
>>>                     self.vm.conn.get_uri(), self.vm.get_uuid())
>>>   +    def control_vm_delete(self, src_ignore):
>>> +        self.emit("action-delete-domain",
>>> +                  self.vm.conn.get_uri(), self.vm.get_uuid())
>>> +
>>>       def control_vm_screenshot(self, src_ignore):
>>>           image = self.console.viewer.get_pixbuf()
>>>   diff --git a/virtManager/engine.py b/virtManager/engine.py
>>> index 16ed552..7ab20e6 100644
>>> --- a/virtManager/engine.py
>>> +++ b/virtManager/engine.py
>>> @@ -48,6 +48,7 @@ from virtManager.create import vmmCreate
>>>   from virtManager.host import vmmHost
>>>   from virtManager.error import vmmErrorDialog
>>>   from virtManager.systray import vmmSystray
>>> +from virtManager.delete import vmmDeleteDialog
>>>     # Enable this to get a report of leaked objects on app shutdown
>>>   # gtk3/pygobject has issues here as of Fedora 18
>>> @@ -95,6 +96,7 @@ class vmmEngine(vmmGObject):
>>>           self.last_timeout = 0
>>>             self.systray = None
>>> +        self.delete_dialog = None
>>>           self.application = Gtk.Application(
>>>                                    application_id="com.redhat.virt-manager",
>>>                                    flags=0)
>>> @@ -239,7 +241,9 @@ class vmmEngine(vmmGObject):
>>>               return
>>>             self.conns[hvuri]["windowDetails"][vmuuid].cleanup()
>>> -        del(self.conns[hvuri]["windowDetails"][vmuuid])
>>> +        if self.conns:
>>> +            # The cleanup call above might end up emptying the conns
>>> dictionary
>>> +            del(self.conns[hvuri]["windowDetails"][vmuuid])
>>>
>> How is this called after cleanup ?
> The cause here is the same as explained for the two patch hunks commented
> above. Even with the two previous patches, if I delete the VM while the
> details window is the only opened window, I got the following error:
> 
> 2013-06-10 18:01:10,171 (engine:408): Exiting app normally.
> 2013-06-10 18:01:10,172 (cliutils:87): Uncaught exception:
> Traceback (most recent call last):
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 244,
> in _do_vm_removed
>     del(self.conns[hvuri]["windowDetails"][vmuuid])
> KeyError: 'qemu+ssh://root@192.168.122.1/system'
> 
> Traceback (most recent call last):
>   File "/home/laggarcia/src/git/virt-manager/virtManager/engine.py", line 244,
> in _do_vm_removed
>     del(self.conns[hvuri]["windowDetails"][vmuuid])
> KeyError: 'qemu+ssh://root@192.168.122.1/system'
> 
> This happens because when vmmEngine._do_vm_removed tries to cleanup the
> details window corresponding to the deleted VM, it will end up calling
> vmmDetails.close, which will, in turn, call vmmEngine.exit_app, as the details
> window is the last one opened, (see my initial comment here). However,
> vmmEngine.exit_app calls vmmEngine._cleanup, which, in turns, has the
> following statement in its last line:
> 
>         self.conns = {}
> 
> Hence, in the sequence of events shot by deleting a VM while the only window
> opened in virt-manager is the details window, vmmEngine.conns will be emptied
> before vmmEngine._do_vm_removed is able to delete the corresponding details
> window from the self.conns dictionary. That's why we need this additional check.
> 
> The best way I found to deal with this sequence of events was to implement the
> simple checks I suggested above. However, I am open to other suggestions here
> as well.

Thanks for the thorough explanations! Indeed all these issues can be hit with
current code: have the last open window be a details window, and 'virsh
undefine' the VM behind virt-manager's back.

I pushed a patch upstream that defers vmmEngine.exit_app to an idle callback,
to let the vmmDetails cleanup bits finish and remove themselves from the
window tracking before we try and do a final cleanup pass. That should fix
these issues.

Thanks,
Cole




More information about the virt-tools-list mailing list