mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-03-08 04:58:29 +03:00
virt-manager: revive cli dbus API (bz 1162815)
This allows virt-manager command line that launch windows to work for an existing virt-manager instance. This is handy for scripting, and giving other apps a reasonable way to launch a virt-manager VM window for example.
This commit is contained in:
parent
f801a7e4ec
commit
a9bc56add3
75
virt-manager
75
virt-manager
@ -91,7 +91,7 @@ def parse_commandline():
|
||||
epilog=epilog)
|
||||
parser.add_argument('--version', action='version',
|
||||
version=CLIConfig.version)
|
||||
parser.set_defaults(uuid=None)
|
||||
parser.set_defaults(domain=None)
|
||||
|
||||
# Trace every libvirt API call to debug output
|
||||
parser.add_argument("--trace-libvirt", dest="tracelibvirt",
|
||||
@ -116,7 +116,7 @@ def parse_commandline():
|
||||
parser.add_argument("--no-fork", action="store_true", dest="nofork",
|
||||
help="Don't fork into background on startup")
|
||||
parser.add_argument("--no-conn-autostart", action="store_true",
|
||||
dest="no_conn_auto", help="Do not autostart connections")
|
||||
dest="skip_autostart", help="Do not autostart connections")
|
||||
parser.add_argument("--spice-disable-auto-usbredir", action="store_true",
|
||||
dest="usbredir", help="Disable Auto USB redirection support")
|
||||
|
||||
@ -134,23 +134,6 @@ def parse_commandline():
|
||||
return parser.parse_known_args()
|
||||
|
||||
|
||||
def launch_specific_window(engine, show, uri, clistr):
|
||||
if not show:
|
||||
return
|
||||
|
||||
logging.debug("Launching requested window '%s'", show)
|
||||
if show == 'creator':
|
||||
engine.show_domain_creator(uri)
|
||||
elif show == 'editor':
|
||||
engine.show_domain_editor(uri, clistr)
|
||||
elif show == 'performance':
|
||||
engine.show_domain_performance(uri, clistr)
|
||||
elif show == 'console':
|
||||
engine.show_domain_console(uri, clistr)
|
||||
elif show == 'summary':
|
||||
engine.show_host_summary(uri)
|
||||
|
||||
|
||||
def main():
|
||||
(options, leftovers) = parse_commandline()
|
||||
|
||||
@ -246,52 +229,34 @@ def main():
|
||||
import virtinst.pollhelpers
|
||||
virtinst.pollhelpers.FORCE_OLD_POLL = bool(options.testoldpoll)
|
||||
|
||||
show = None
|
||||
show_window = None
|
||||
domain = None
|
||||
if options.show_domain_creator:
|
||||
show = "creator"
|
||||
elif options.show_domain_editor:
|
||||
show = "editor"
|
||||
elif options.show_domain_performance:
|
||||
show = "performance"
|
||||
elif options.show_domain_console:
|
||||
show = "console"
|
||||
show_window = vmmEngine.CLI_SHOW_DOMAIN_CREATOR
|
||||
elif options.show_host_summary:
|
||||
show = "summary"
|
||||
show_window = vmmEngine.CLI_SHOW_HOST_SUMMARY
|
||||
elif options.show_domain_editor:
|
||||
show_window = vmmEngine.CLI_SHOW_DOMAIN_EDITOR
|
||||
domain = options.show_domain_editor
|
||||
elif options.show_domain_performance:
|
||||
show_window = vmmEngine.CLI_SHOW_DOMAIN_PERFORMANCE
|
||||
domain = options.show_domain_performance
|
||||
elif options.show_domain_console:
|
||||
show_window = vmmEngine.CLI_SHOW_DOMAIN_CONSOLE
|
||||
domain = options.show_domain_console
|
||||
|
||||
if show and options.uri is None:
|
||||
if show_window and options.uri is None:
|
||||
raise RuntimeError("can't use --show-* options without --connect")
|
||||
if show:
|
||||
options.uuid = (options.uuid or options.show_domain_creator or
|
||||
options.show_domain_editor or
|
||||
options.show_domain_performance or
|
||||
options.show_domain_console or
|
||||
options.show_host_summary)
|
||||
|
||||
# Hook libvirt events into glib main loop
|
||||
LibvirtGLib.init(None)
|
||||
LibvirtGLib.event_register()
|
||||
|
||||
engine = vmmEngine()
|
||||
engine.skip_autostart = options.no_conn_auto
|
||||
engine.uri_at_startup = options.uri
|
||||
from virtManager.dbusapi import StartupAPI
|
||||
api = StartupAPI()
|
||||
|
||||
if show:
|
||||
def cb(conn):
|
||||
if conn.is_disconnected():
|
||||
# Connection error
|
||||
return True
|
||||
|
||||
if conn.is_active():
|
||||
launch_specific_window(engine, show, options.uri, options.uuid)
|
||||
return True
|
||||
|
||||
return False
|
||||
engine.uri_cb = cb
|
||||
engine.show_manager_window = False
|
||||
engine.skip_autostart = True
|
||||
|
||||
# Finally start the app for real
|
||||
engine.application.run(None)
|
||||
api.run_cli_command(options.uri, show_window, domain)
|
||||
api.start(options.skip_autostart)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
147
virtManager/dbusapi.py
Normal file
147
virtManager/dbusapi.py
Normal file
@ -0,0 +1,147 @@
|
||||
#
|
||||
# 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)
|
@ -63,6 +63,12 @@ DETAILS_CONSOLE = 3
|
||||
|
||||
|
||||
class vmmEngine(vmmGObject):
|
||||
CLI_SHOW_DOMAIN_CREATOR = "creator"
|
||||
CLI_SHOW_DOMAIN_EDITOR = "editor"
|
||||
CLI_SHOW_DOMAIN_PERFORMANCE = "performance"
|
||||
CLI_SHOW_DOMAIN_CONSOLE = "console"
|
||||
CLI_SHOW_HOST_SUMMARY = "summary"
|
||||
|
||||
__gsignals__ = {
|
||||
"conn-added": (GObject.SignalFlags.RUN_FIRST, None, [object]),
|
||||
"conn-removed": (GObject.SignalFlags.RUN_FIRST, None, [str]),
|
||||
@ -87,10 +93,10 @@ class vmmEngine(vmmGObject):
|
||||
|
||||
self.systray = None
|
||||
self.delete_dialog = None
|
||||
self.application = Gtk.Application(
|
||||
application_id="com.redhat.virt-manager",
|
||||
flags=0)
|
||||
self.application.connect("activate", self._activate)
|
||||
self._application = Gtk.Application(
|
||||
application_id="org.virt-manager.gtkapplication",
|
||||
flags=0)
|
||||
self._application.connect("activate", self._activate)
|
||||
self._appwindow = Gtk.Window()
|
||||
|
||||
self._tick_counter = 0
|
||||
@ -109,11 +115,9 @@ class vmmEngine(vmmGObject):
|
||||
# keep running in system tray if enabled
|
||||
self.windows = 0
|
||||
|
||||
# Public bits set by virt-manager cli
|
||||
self.skip_autostart = False
|
||||
self.uri_at_startup = None
|
||||
self.uri_cb = None
|
||||
self.show_manager_window = True
|
||||
self._cli_uri = None
|
||||
self._show_manager_at_startup = True
|
||||
self._skip_autostart = False
|
||||
|
||||
self.init_systray()
|
||||
|
||||
@ -130,23 +134,18 @@ class vmmEngine(vmmGObject):
|
||||
|
||||
|
||||
def _activate(self, ignore):
|
||||
if self.show_manager_window:
|
||||
self.show_manager()
|
||||
if self._show_manager_at_startup:
|
||||
self._show_manager()
|
||||
else:
|
||||
self.get_manager()
|
||||
self.application.add_window(self._appwindow)
|
||||
self._application.add_window(self._appwindow)
|
||||
|
||||
if self.uri_at_startup:
|
||||
conn = self.make_conn(self.uri_at_startup)
|
||||
self.register_conn(conn, skip_config=True)
|
||||
if conn and self.uri_cb:
|
||||
conn.connect_opt_out("state-changed", self.uri_cb)
|
||||
|
||||
self.connect_to_uri(self.uri_at_startup)
|
||||
|
||||
if not self.skip_autostart:
|
||||
if not self._skip_autostart and not self._cli_uri:
|
||||
self.autostart_conns()
|
||||
|
||||
def start(self, skip_autostart):
|
||||
self._skip_autostart = skip_autostart
|
||||
self._application.run(None)
|
||||
|
||||
def init_systray(self):
|
||||
if self.systray:
|
||||
@ -176,11 +175,11 @@ class vmmEngine(vmmGObject):
|
||||
systray_enabled = self.config.get_view_system_tray()
|
||||
if self.windows == 0 and not systray_enabled:
|
||||
# Show the manager so that the user can control the application
|
||||
self.show_manager()
|
||||
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.uri_at_startup:
|
||||
if self.config.get_conn_uris() or self._cli_uri:
|
||||
return
|
||||
|
||||
self.timeout_add(1000, self._add_default_conn, manager)
|
||||
@ -468,7 +467,7 @@ class vmmEngine(vmmGObject):
|
||||
for ignore in range(Gtk.main_level()):
|
||||
Gtk.main_quit()
|
||||
|
||||
self.application.remove_window(self._appwindow)
|
||||
self._application.remove_window(self._appwindow)
|
||||
|
||||
def _create_inspection_thread(self):
|
||||
logging.debug("libguestfs inspection support: %s",
|
||||
@ -851,7 +850,7 @@ class vmmEngine(vmmGObject):
|
||||
self.connect("conn-added", obj.add_conn)
|
||||
self.connect("conn-removed", obj.remove_conn)
|
||||
|
||||
obj.set_initial_selection(self.uri_at_startup)
|
||||
obj.set_initial_selection(self._cli_uri)
|
||||
|
||||
self.windowManager = obj
|
||||
return self.windowManager
|
||||
@ -919,17 +918,6 @@ class vmmEngine(vmmGObject):
|
||||
# Window launchers from virt-manager cli #
|
||||
##########################################
|
||||
|
||||
def show_manager(self):
|
||||
self._do_show_manager(None)
|
||||
|
||||
def show_host_summary(self, uri):
|
||||
self._do_show_host(self.get_manager(), uri)
|
||||
|
||||
def show_domain_creator(self, uri):
|
||||
self.show_manager()
|
||||
self._do_show_create(self.get_manager(), uri)
|
||||
|
||||
|
||||
def _find_vm_by_cli_str(self, uri, clistr):
|
||||
"""
|
||||
Lookup a VM by a string passed in on the CLI. Can be either
|
||||
@ -958,15 +946,74 @@ class vmmEngine(vmmGObject):
|
||||
|
||||
self._show_vm_helper(src, uri, vm, page, True)
|
||||
|
||||
def show_domain_console(self, uri, clistr):
|
||||
def _show_manager(self):
|
||||
self._do_show_manager(None)
|
||||
|
||||
def _show_host_summary(self, uri):
|
||||
self._do_show_host(self.get_manager(), uri)
|
||||
|
||||
def _show_domain_creator(self, uri):
|
||||
self._show_manager()
|
||||
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)
|
||||
|
||||
def show_domain_editor(self, uri, clistr):
|
||||
def _show_domain_editor(self, uri, clistr):
|
||||
self.idle_add(self._cli_show_vm_helper, uri, clistr, DETAILS_CONFIG)
|
||||
|
||||
def show_domain_performance(self, uri, clistr):
|
||||
def _show_domain_performance(self, uri, clistr):
|
||||
self.idle_add(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)
|
||||
|
||||
def _cli_conn_connected_cb(self, conn, uri, show_window, domain):
|
||||
ignore = conn
|
||||
|
||||
if conn.is_disconnected():
|
||||
# Connection error
|
||||
return True
|
||||
|
||||
if conn.is_active():
|
||||
self._launch_cli_window(uri, show_window, domain)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def run_cli_command(self, uri, show_window, domain):
|
||||
if not uri:
|
||||
return
|
||||
|
||||
self._cli_uri = uri
|
||||
conn = self.make_conn(uri)
|
||||
self.register_conn(conn, skip_config=True)
|
||||
if show_window:
|
||||
if conn.is_active():
|
||||
self.idle_add(self._launch_cli_window,
|
||||
uri, show_window, domain)
|
||||
else:
|
||||
conn.connect_opt_out("state-changed",
|
||||
self._cli_conn_connected_cb, uri, show_window, domain)
|
||||
self._show_manager_at_startup = False
|
||||
|
||||
if conn.is_disconnected():
|
||||
def connect():
|
||||
self.connect_to_uri(uri)
|
||||
self.idle_add(connect)
|
||||
|
||||
|
||||
#######################################
|
||||
# Domain actions run/destroy/save ... #
|
||||
|
Loading…
x
Reference in New Issue
Block a user