mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-03-08 04:58:29 +03:00
virt-manager: Drop manual dbusapi, use GtkApplication
So I was confused here and completely missed that GtkApplication is meant to handle the common case of invoking actions in an existing app instance. So drop the manual dbus server and use the simpler Gtk support. Fix a bunch of other errors in this area too
This commit is contained in:
parent
7ef17138da
commit
febe0abdd2
@ -252,11 +252,9 @@ def main():
|
||||
LibvirtGLib.init(None)
|
||||
LibvirtGLib.event_register()
|
||||
|
||||
from virtManager.dbusapi import StartupAPI
|
||||
api = StartupAPI()
|
||||
engine = vmmEngine()
|
||||
|
||||
api.run_cli_command(options.uri, show_window, domain)
|
||||
api.start(options.skip_autostart)
|
||||
engine.start(options.uri, show_window, domain, options.skip_autostart)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -1,147 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2015 Red Hat, Inc.
|
||||
# Copyright (C) 2015 Cole Robinson <crobinso@redhat.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||
# MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from gi.repository import Gio
|
||||
|
||||
from virtManager.engine import vmmEngine
|
||||
|
||||
|
||||
class _DBusServer(object):
|
||||
"""
|
||||
Initializing this object starts the dbus service. It's job is just
|
||||
to proxy run_cli_command calls from a virt-manager command line
|
||||
invocation to the already running virt-manager instance
|
||||
"""
|
||||
SERVICE_NAME = "org.virt-manager.cli"
|
||||
OBJECT_PATH = "/org/virtmanager/cli"
|
||||
INTERFACE_NAME = "org.virtmanager.cli"
|
||||
|
||||
API_XML = """
|
||||
<node>
|
||||
<interface name='%s'>
|
||||
<method name='run_cli_command'>
|
||||
<arg type='s' name='uri' direction='in'/>
|
||||
<arg type='s' name='show_window' direction='in'/>
|
||||
<arg type='s' name='domain' direction='in'/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
""" % INTERFACE_NAME
|
||||
|
||||
def __init__(self, engine):
|
||||
self.engine = engine
|
||||
logging.debug("Starting dbus cli server")
|
||||
|
||||
Gio.bus_own_name(
|
||||
Gio.BusType.SESSION,
|
||||
self.SERVICE_NAME,
|
||||
Gio.BusNameOwnerFlags.NONE,
|
||||
self._on_bus_acquired,
|
||||
self._on_name_acquired,
|
||||
self._on_name_lost)
|
||||
|
||||
|
||||
def _handle_method_call(self,
|
||||
connection, sender, object_path, interface_name,
|
||||
method_name, parameters, invocation):
|
||||
ignore = connection
|
||||
ignore = sender
|
||||
ignore = object_path
|
||||
ignore = interface_name
|
||||
|
||||
try:
|
||||
if method_name == "run_cli_command":
|
||||
logging.debug("dbus run_cli_command invoked with args=%s",
|
||||
parameters)
|
||||
self.engine.run_cli_command(*parameters)
|
||||
invocation.return_value(None)
|
||||
else:
|
||||
raise RuntimeError("Unhandled method=%s" % method_name)
|
||||
except Exception, e:
|
||||
logging.debug("Error processing dbus method=%s",
|
||||
method_name, exc_info=True)
|
||||
Gio.DBusMethodInvocation.return_error_literal(
|
||||
invocation, Gio.DBusError.quark(), Gio.DBusError.FAILED, str(e))
|
||||
|
||||
|
||||
def _on_bus_acquired(self, connection, name):
|
||||
ignore = name
|
||||
introspection_data = Gio.DBusNodeInfo.new_for_xml(self.API_XML)
|
||||
connection.register_object(
|
||||
self.OBJECT_PATH,
|
||||
introspection_data.interfaces[0],
|
||||
self._handle_method_call, None, None)
|
||||
|
||||
def _on_name_acquired(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def _on_name_lost(self, *args, **kwargs):
|
||||
logging.debug("Failed to acquire dbus service args=%s kwargs=%s",
|
||||
args, kwargs)
|
||||
|
||||
|
||||
class StartupAPI(object):
|
||||
def __init__(self):
|
||||
self._engine = vmmEngine()
|
||||
self._proxy = self._init_dbus()
|
||||
|
||||
|
||||
#################
|
||||
# Dbus handling #
|
||||
#################
|
||||
|
||||
def _init_dbus(self):
|
||||
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
||||
proxy = Gio.DBusProxy.new_sync(
|
||||
bus, 0, None,
|
||||
"org.freedesktop.DBus",
|
||||
"/org/freedesktop/DBus",
|
||||
"org.freedesktop.DBus")
|
||||
|
||||
if not proxy.NameHasOwner("(s)", _DBusServer.SERVICE_NAME):
|
||||
_DBusServer(self._engine)
|
||||
return
|
||||
|
||||
logging.debug("Detected app is already running, connecting "
|
||||
"to existing instance.")
|
||||
return Gio.DBusProxy.new_sync(
|
||||
bus, 0, None,
|
||||
_DBusServer.SERVICE_NAME,
|
||||
_DBusServer.OBJECT_PATH,
|
||||
_DBusServer.INTERFACE_NAME)
|
||||
|
||||
|
||||
##############
|
||||
# Public API #
|
||||
##############
|
||||
|
||||
def start(self, skip_autostart):
|
||||
# Unconditionally use the engine here, since GtkApplication already
|
||||
# provides us with app uniqueness checking.
|
||||
self._engine.start(skip_autostart)
|
||||
|
||||
def run_cli_command(self, uri, show_window, domain):
|
||||
if self._proxy:
|
||||
self._proxy.run_cli_command("(sss)",
|
||||
uri or "", show_window or "", domain or "")
|
||||
else:
|
||||
self._engine.run_cli_command(uri, show_window, domain)
|
@ -18,6 +18,7 @@
|
||||
# MA 02110-1301 USA.
|
||||
#
|
||||
|
||||
from gi.repository import Gio
|
||||
from gi.repository import GLib
|
||||
from gi.repository import GObject
|
||||
from gi.repository import Gtk
|
||||
@ -93,11 +94,9 @@ class vmmEngine(vmmGObject):
|
||||
|
||||
self.systray = None
|
||||
self.delete_dialog = None
|
||||
self._application = Gtk.Application(
|
||||
application_id="org.virt-manager.gtkapplication",
|
||||
flags=0)
|
||||
self._application.connect("activate", self._activate)
|
||||
self._appwindow = Gtk.Window()
|
||||
|
||||
self._gtkapplication = None
|
||||
self._init_gtk_application()
|
||||
|
||||
self._tick_counter = 0
|
||||
self._tick_thread_slow = False
|
||||
@ -115,10 +114,6 @@ class vmmEngine(vmmGObject):
|
||||
# keep running in system tray if enabled
|
||||
self.windows = 0
|
||||
|
||||
self._cli_uri = None
|
||||
self._show_manager_at_startup = True
|
||||
self._skip_autostart = False
|
||||
|
||||
self.init_systray()
|
||||
|
||||
self.add_gsettings_handle(
|
||||
@ -133,20 +128,59 @@ class vmmEngine(vmmGObject):
|
||||
self.tick()
|
||||
|
||||
|
||||
def _activate(self, ignore):
|
||||
if self._show_manager_at_startup:
|
||||
self._show_manager()
|
||||
############################
|
||||
# Gtk Application handling #
|
||||
############################
|
||||
|
||||
def _on_gtk_application_activated(self, ignore):
|
||||
"""
|
||||
Invoked after application.run()
|
||||
"""
|
||||
if not self._application.get_windows():
|
||||
logging.debug("Initial gtkapplication activated")
|
||||
self._application.add_window(Gtk.Window())
|
||||
|
||||
def _init_gtk_application(self):
|
||||
self._application = Gtk.Application(
|
||||
application_id="org.virt-manager.virt-manager", flags=0)
|
||||
self._application.register(None)
|
||||
self._application.connect("activate",
|
||||
self._on_gtk_application_activated)
|
||||
|
||||
action = Gio.SimpleAction.new("cli_command",
|
||||
GLib.VariantType.new("(sss)"))
|
||||
action.connect("activate", self._handle_cli_command)
|
||||
self._application.add_action(action)
|
||||
|
||||
def _default_startup(self, skip_autostart):
|
||||
uris = self.conns.keys()
|
||||
if not uris:
|
||||
logging.debug("No stored URIs found.")
|
||||
else:
|
||||
self.get_manager()
|
||||
self._application.add_window(self._appwindow)
|
||||
logging.debug("Loading stored URIs:\n%s",
|
||||
" \n".join(sorted(uris)))
|
||||
|
||||
if not self._skip_autostart and not self._cli_uri:
|
||||
self.autostart_conns()
|
||||
if not skip_autostart:
|
||||
self.idle_add(self.autostart_conns)
|
||||
|
||||
def start(self, skip_autostart):
|
||||
self._skip_autostart = skip_autostart
|
||||
if not self.config.get_conn_uris():
|
||||
# Only add default if no connections are currently known
|
||||
self.timeout_add(1000, self._add_default_conn)
|
||||
|
||||
def start(self, uri, show_window, domain, skip_autostart):
|
||||
# Dispatch dbus CLI command
|
||||
data = GLib.Variant("(sss)",
|
||||
(uri or "", show_window or "", domain or ""))
|
||||
self._application.activate_action("cli_command", data)
|
||||
|
||||
if self._application.get_is_remote():
|
||||
logging.debug("Connected to remote app instance.")
|
||||
return
|
||||
|
||||
self._default_startup(skip_autostart)
|
||||
self._application.run(None)
|
||||
|
||||
|
||||
def init_systray(self):
|
||||
if self.systray:
|
||||
return
|
||||
@ -177,14 +211,9 @@ class vmmEngine(vmmGObject):
|
||||
# Show the manager so that the user can control the application
|
||||
self._show_manager()
|
||||
|
||||
def add_default_conn(self, manager):
|
||||
# Only add default if no connections are currently known
|
||||
if self.config.get_conn_uris() or self._cli_uri:
|
||||
return
|
||||
def _add_default_conn(self):
|
||||
manager = self.get_manager()
|
||||
|
||||
self.timeout_add(1000, self._add_default_conn, manager)
|
||||
|
||||
def _add_default_conn(self, manager):
|
||||
# Manager fail message
|
||||
msg = _("Could not detect a default hypervisor. Make\n"
|
||||
"sure the appropriate virtualization packages\n"
|
||||
@ -230,10 +259,7 @@ class vmmEngine(vmmGObject):
|
||||
|
||||
|
||||
def load_stored_uris(self):
|
||||
uris = self.config.get_conn_uris()
|
||||
if not uris:
|
||||
return
|
||||
logging.debug("About to connect to uris %s", uris)
|
||||
uris = self.config.get_conn_uris() or []
|
||||
for uri in uris:
|
||||
conn = self.make_conn(uri)
|
||||
self.register_conn(conn, skip_config=True)
|
||||
@ -378,10 +404,7 @@ class vmmEngine(vmmGObject):
|
||||
self.windows -= 1
|
||||
logging.debug("window counter decremented to %s", self.windows)
|
||||
|
||||
if self._can_exit():
|
||||
# Defer this to an idle callback, since we can race with
|
||||
# a vmmDetails window being deleted.
|
||||
self.idle_add(self.exit_app, src)
|
||||
self._exit_app_if_no_windows(src)
|
||||
|
||||
def _can_exit(self):
|
||||
# Don't exit if system tray is enabled
|
||||
@ -438,6 +461,13 @@ class vmmEngine(vmmGObject):
|
||||
self.cleanup_conn(uri)
|
||||
self.conns = {}
|
||||
|
||||
def _exit_app_if_no_windows(self, src=None):
|
||||
def cb():
|
||||
if self._can_exit():
|
||||
logging.debug("No windows found, requesting app exit")
|
||||
self.exit_app(src or self)
|
||||
self.idle_add(cb)
|
||||
|
||||
def exit_app(self, src):
|
||||
if self.err is None:
|
||||
# Already in cleanup
|
||||
@ -467,7 +497,7 @@ class vmmEngine(vmmGObject):
|
||||
for ignore in range(Gtk.main_level()):
|
||||
Gtk.main_quit()
|
||||
|
||||
self._application.remove_window(self._appwindow)
|
||||
self._application.quit()
|
||||
|
||||
def _create_inspection_thread(self):
|
||||
logging.debug("libguestfs inspection support: %s",
|
||||
@ -682,7 +712,7 @@ class vmmEngine(vmmGObject):
|
||||
else:
|
||||
if self._can_exit():
|
||||
self.err.show_err(msg, details, title, modal=True)
|
||||
self.idle_add(self.exit_app, conn)
|
||||
self._exit_app_if_no_windows(conn)
|
||||
else:
|
||||
self.err.show_err(msg, details, title)
|
||||
|
||||
@ -810,9 +840,6 @@ class vmmEngine(vmmGObject):
|
||||
details.show()
|
||||
except Exception, e:
|
||||
src.err.show_err(_("Error launching details: %s") % str(e))
|
||||
finally:
|
||||
if self._can_exit():
|
||||
self.idle_add(self.exit_app, src)
|
||||
|
||||
def _do_show_vm(self, src, uri, connkey):
|
||||
conn = self._lookup_conn(uri)
|
||||
@ -845,13 +872,10 @@ class vmmEngine(vmmGObject):
|
||||
obj.connect("manager-opened", self.increment_window_counter)
|
||||
obj.connect("manager-closed", self.decrement_window_counter)
|
||||
obj.connect("remove-conn", self.remove_conn)
|
||||
obj.connect("add-default-conn", self.add_default_conn)
|
||||
|
||||
self.connect("conn-added", obj.add_conn)
|
||||
self.connect("conn-removed", obj.remove_conn)
|
||||
|
||||
obj.set_initial_selection(self._cli_uri)
|
||||
|
||||
self.windowManager = obj
|
||||
return self.windowManager
|
||||
|
||||
@ -941,7 +965,6 @@ class vmmEngine(vmmGObject):
|
||||
if not vm:
|
||||
src.err.show_err("%s does not have VM '%s'" %
|
||||
(uri, clistr), modal=True)
|
||||
self.exit_app(src.err)
|
||||
return
|
||||
|
||||
self._show_vm_helper(src, uri, vm, page, True)
|
||||
@ -957,49 +980,76 @@ class vmmEngine(vmmGObject):
|
||||
self._do_show_create(self.get_manager(), uri)
|
||||
|
||||
def _show_domain_console(self, uri, clistr):
|
||||
self.idle_add(self._cli_show_vm_helper, uri, clistr, DETAILS_CONSOLE)
|
||||
self._cli_show_vm_helper(uri, clistr, DETAILS_CONSOLE)
|
||||
|
||||
def _show_domain_editor(self, uri, clistr):
|
||||
self.idle_add(self._cli_show_vm_helper, uri, clistr, DETAILS_CONFIG)
|
||||
self._cli_show_vm_helper(uri, clistr, DETAILS_CONFIG)
|
||||
|
||||
def _show_domain_performance(self, uri, clistr):
|
||||
self.idle_add(self._cli_show_vm_helper, uri, clistr, DETAILS_PERF)
|
||||
self._cli_show_vm_helper(uri, clistr, DETAILS_PERF)
|
||||
|
||||
def _launch_cli_window(self, uri, show_window, clistr):
|
||||
logging.debug("Launching requested window '%s'", show_window)
|
||||
if show_window == self.CLI_SHOW_DOMAIN_CREATOR:
|
||||
self._show_domain_creator(uri)
|
||||
elif show_window == self.CLI_SHOW_DOMAIN_EDITOR:
|
||||
self._show_domain_editor(uri, clistr)
|
||||
elif show_window == self.CLI_SHOW_DOMAIN_PERFORMANCE:
|
||||
self._show_domain_performance(uri, clistr)
|
||||
elif show_window == self.CLI_SHOW_DOMAIN_CONSOLE:
|
||||
self._show_domain_console(uri, clistr)
|
||||
elif show_window == self.CLI_SHOW_HOST_SUMMARY:
|
||||
self._show_host_summary(uri)
|
||||
else:
|
||||
logging.debug("Unknown cli window command '%s'", show_window)
|
||||
try:
|
||||
logging.debug("Launching requested window '%s'", show_window)
|
||||
if show_window == self.CLI_SHOW_DOMAIN_CREATOR:
|
||||
self._show_domain_creator(uri)
|
||||
elif show_window == self.CLI_SHOW_DOMAIN_EDITOR:
|
||||
self._show_domain_editor(uri, clistr)
|
||||
elif show_window == self.CLI_SHOW_DOMAIN_PERFORMANCE:
|
||||
self._show_domain_performance(uri, clistr)
|
||||
elif show_window == self.CLI_SHOW_DOMAIN_CONSOLE:
|
||||
self._show_domain_console(uri, clistr)
|
||||
elif show_window == self.CLI_SHOW_HOST_SUMMARY:
|
||||
self._show_host_summary(uri)
|
||||
else:
|
||||
raise RuntimeError("Unknown cli window command '%s'" %
|
||||
show_window)
|
||||
finally:
|
||||
# In case of cli error, we may need to exit the app
|
||||
self._exit_app_if_no_windows()
|
||||
|
||||
def _cli_conn_connected_cb(self, conn, uri, show_window, domain):
|
||||
ignore = conn
|
||||
try:
|
||||
ignore = conn
|
||||
|
||||
if conn.is_disconnected():
|
||||
# Connection error
|
||||
if conn.is_disconnected():
|
||||
raise RuntimeError("failed to connect to cli uri=%s" % uri)
|
||||
|
||||
if conn.is_active():
|
||||
self._launch_cli_window(uri, show_window, domain)
|
||||
return True
|
||||
|
||||
return False
|
||||
except:
|
||||
# In case of cli error, we may need to exit the app
|
||||
logging.debug("Error in cli connection callback", exc_info=True)
|
||||
self._exit_app_if_no_windows()
|
||||
return True
|
||||
|
||||
if conn.is_active():
|
||||
self._launch_cli_window(uri, show_window, domain)
|
||||
return True
|
||||
def _do_handle_cli_command(self, actionobj, variant):
|
||||
ignore = actionobj
|
||||
uri = variant[0]
|
||||
show_window = variant[1]
|
||||
domain = variant[2]
|
||||
|
||||
return False
|
||||
|
||||
def run_cli_command(self, uri, show_window, domain):
|
||||
logging.debug("processing cli command uri=%s show_window=%s domain=%s",
|
||||
uri, show_window, domain)
|
||||
if not uri:
|
||||
logging.debug("No cli action requested, launching default window")
|
||||
self._show_manager()
|
||||
return
|
||||
|
||||
self._cli_uri = uri
|
||||
conn = self.make_conn(uri)
|
||||
self.register_conn(conn, skip_config=True)
|
||||
|
||||
if conn.is_disconnected():
|
||||
# Schedule connection open
|
||||
def connect():
|
||||
# We need to wrap this to ignore the return value, otherwise
|
||||
# the callback will be rescheduled
|
||||
self.connect_to_uri(uri)
|
||||
self.idle_add(connect)
|
||||
|
||||
if show_window:
|
||||
if conn.is_active():
|
||||
self.idle_add(self._launch_cli_window,
|
||||
@ -1007,12 +1057,17 @@ class vmmEngine(vmmGObject):
|
||||
else:
|
||||
conn.connect_opt_out("state-changed",
|
||||
self._cli_conn_connected_cb, uri, show_window, domain)
|
||||
self._show_manager_at_startup = False
|
||||
else:
|
||||
self.get_manager().set_initial_selection(uri)
|
||||
self._show_manager()
|
||||
|
||||
if conn.is_disconnected():
|
||||
def connect():
|
||||
self.connect_to_uri(uri)
|
||||
self.idle_add(connect)
|
||||
def _handle_cli_command(self, actionobj, variant):
|
||||
try:
|
||||
return self._do_handle_cli_command(actionobj, variant)
|
||||
except:
|
||||
# In case of cli error, we may need to exit the app
|
||||
logging.debug("Error handling cli command", exc_info=True)
|
||||
self._exit_app_if_no_windows()
|
||||
|
||||
|
||||
#######################################
|
||||
|
@ -107,7 +107,6 @@ class vmmManager(vmmGObjectUI):
|
||||
"manager-closed": (GObject.SignalFlags.RUN_FIRST, None, []),
|
||||
"manager-opened": (GObject.SignalFlags.RUN_FIRST, None, []),
|
||||
"remove-conn": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
||||
"add-default-conn": (GObject.SignalFlags.RUN_FIRST, None, []),
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@ -190,8 +189,6 @@ class vmmManager(vmmGObjectUI):
|
||||
self.enable_polling(COL_NETWORK)
|
||||
self.enable_polling(COL_MEM)
|
||||
|
||||
# Queue up the default connection detector
|
||||
self.idle_emit("add-default-conn")
|
||||
|
||||
##################
|
||||
# Common methods #
|
||||
|
Loading…
x
Reference in New Issue
Block a user