From 7a47a4da08acf17efd95f5afc2aeb220ee31443c Mon Sep 17 00:00:00 2001 From: Pavel Grunt Date: Fri, 26 Sep 2014 15:55:52 +0200 Subject: [PATCH] Show VM chooser dialog when oVirt VM name is missing When a user tries to connect to ovirt without specifying VM name (remote-viewer ovirt://ovirt.example.com) or with wrong VM name a list of available virtual machines is shown, and the user may pick a machine he wants to connect to. --- po/POTFILES.in | 1 + src/Makefile.am | 2 + src/remote-viewer.c | 125 ++++++++++++++++++++++++------ src/virt-viewer-util.h | 1 + src/virt-viewer-vm-connection.c | 105 +++++++++++++++++++++++++ src/virt-viewer-vm-connection.h | 36 +++++++++ src/virt-viewer-vm-connection.xml | 119 ++++++++++++++++++++++++++++ 7 files changed, 367 insertions(+), 22 deletions(-) create mode 100644 src/virt-viewer-vm-connection.c create mode 100644 src/virt-viewer-vm-connection.h create mode 100644 src/virt-viewer-vm-connection.xml diff --git a/po/POTFILES.in b/po/POTFILES.in index 06f8ad1..0f2b6bf 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -15,3 +15,4 @@ src/virt-viewer-file.c src/virt-viewer.c [type: gettext/glade] src/virt-viewer.xml [type: gettext/glade] src/virt-viewer-guest-details.xml +[type: gettext/glade] src/virt-viewer-vm-connection.xml diff --git a/src/Makefile.am b/src/Makefile.am index c425522..e76da2c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,7 @@ builderxml_DATA = \ virt-viewer-about.xml \ virt-viewer-auth.xml \ virt-viewer-guest-details.xml \ + virt-viewer-vm-connection.xml \ $(NULL) EXTRA_DIST = \ @@ -46,6 +47,7 @@ COMMON_SOURCES = \ virt-viewer-display.h virt-viewer-display.c \ virt-viewer-notebook.h virt-viewer-notebook.c \ virt-viewer-window.h virt-viewer-window.c \ + virt-viewer-vm-connection.h virt-viewer-vm-connection.c \ view/autoDrawer.c \ view/autoDrawer.h \ view/drawer.c \ diff --git a/src/remote-viewer.c b/src/remote-viewer.c index 49981aa..485baf2 100644 --- a/src/remote-viewer.c +++ b/src/remote-viewer.c @@ -31,6 +31,7 @@ #ifdef HAVE_OVIRT #include #include "ovirt-foreign-menu.h" +#include "virt-viewer-vm-connection.h" #endif #ifdef HAVE_SPICE_GTK @@ -74,6 +75,10 @@ enum { PROP_OPEN_RECENT_DIALOG }; +#ifdef HAVE_OVIRT +static OvirtVm * choose_vm(char **vm_name, OvirtCollection *vms, GError **error); +#endif + static gboolean remote_viewer_start(VirtViewerApp *self); #ifdef HAVE_SPICE_GTK static gboolean remote_viewer_activate(VirtViewerApp *self, GError **error); @@ -648,19 +653,27 @@ parse_ovirt_uri(const gchar *uri_str, char **rest_uri, char **name, char **usern g_return_val_if_fail(name != NULL, FALSE); uri = xmlParseURI(uri_str); - if (uri == NULL) - return FALSE; + g_return_val_if_fail(uri != NULL, FALSE); if (g_strcmp0(uri->scheme, "ovirt") != 0) { xmlFreeURI(uri); return FALSE; } + if (username && uri->user) + *username = g_strdup(uri->user); + if (uri->path == NULL) { + *name = NULL; + *rest_uri = g_strdup_printf("https://%s/api/", uri->server); + xmlFreeURI(uri); + return TRUE; + } + + if (*uri->path != '/') { xmlFreeURI(uri); return FALSE; } - g_return_val_if_fail(*uri->path == '/', FALSE); /* extract VM name from path */ path_elements = g_strsplit(uri->path, "/", -1); @@ -668,15 +681,11 @@ parse_ovirt_uri(const gchar *uri_str, char **rest_uri, char **name, char **usern element_count = g_strv_length(path_elements); if (element_count == 0) { g_strfreev(path_elements); - xmlFreeURI(uri); return FALSE; } vm_name = path_elements[element_count-1]; path_elements[element_count-1] = NULL; - if (username && uri->user) - *username = g_strdup(uri->user); - /* build final URI */ rel_path = g_strjoinv("/", path_elements); /* FIXME: how to decide between http and https? */ @@ -799,7 +808,7 @@ virt_viewer_app_set_ovirt_foreign_menu(VirtViewerApp *app, static gboolean -create_ovirt_session(VirtViewerApp *app, const char *uri) +create_ovirt_session(VirtViewerApp *app, const char *uri, GError **err) { OvirtProxy *proxy = NULL; OvirtApi *api = NULL; @@ -822,15 +831,17 @@ create_ovirt_session(VirtViewerApp *app, const char *uri) gchar *ghost = NULL; gchar *ticket = NULL; gchar *host_subject = NULL; + gchar *guid = NULL; g_return_val_if_fail(VIRT_VIEWER_IS_APP(app), FALSE); - if (!parse_ovirt_uri(uri, &rest_uri, &vm_name, &username)) - goto error; - proxy = ovirt_proxy_new(rest_uri); - if (proxy == NULL) + if (!parse_ovirt_uri(uri, &rest_uri, &vm_name, &username)) { + g_set_error_literal(&error, VIRT_VIEWER_ERROR, VIRT_VIEWER_ERROR_FAILED, + _("failed to parse ovirt uri")); goto error; + } + proxy = ovirt_proxy_new(rest_uri); g_object_set(proxy, "username", username, NULL); @@ -849,24 +860,38 @@ create_ovirt_session(VirtViewerApp *app, const char *uri) g_debug("failed to lookup %s: %s", vm_name, error->message); goto error; } - vm = OVIRT_VM(ovirt_collection_lookup_resource(vms, vm_name)); - g_return_val_if_fail(vm != NULL, FALSE); + if (vm_name == NULL || + (vm = OVIRT_VM(ovirt_collection_lookup_resource(vms, vm_name))) == NULL) { + vm = choose_vm(&vm_name, vms, &error); + if (vm == NULL) { + goto error; + } + } g_object_get(G_OBJECT(vm), "state", &state, NULL); if (state != OVIRT_VM_STATE_UP) { - g_debug("oVirt VM %s is not running", vm_name); + g_set_error(&error, VIRT_VIEWER_ERROR, VIRT_VIEWER_ERROR_FAILED, + _("oVirt VM %s is not running"), vm_name); + g_debug(error->message); goto error; } + g_object_set(app, "guest-name", vm_name, NULL); if (!ovirt_vm_get_ticket(vm, proxy, &error)) { g_debug("failed to get ticket for %s: %s", vm_name, error->message); goto error; } - g_object_get(G_OBJECT(vm), "display", &display, NULL); + g_object_get(G_OBJECT(vm), "display", &display, "guid", &guid, NULL); if (display == NULL) { + g_set_error(&error, VIRT_VIEWER_ERROR, VIRT_VIEWER_ERROR_FAILED, + _("oVirt VM %s has no display"), vm_name); goto error; } + if (guid != NULL) { + g_object_set(app, "uuid", guid, NULL); + } + g_object_get(G_OBJECT(display), "type", &type, "address", &ghost, @@ -883,7 +908,9 @@ create_ovirt_session(VirtViewerApp *app, const char *uri) } else if (type == OVIRT_VM_DISPLAY_VNC) { session_type = "vnc"; } else { - g_debug("Unknown display type: %d", type); + g_set_error(&error, VIRT_VIEWER_ERROR, VIRT_VIEWER_ERROR_FAILED, + _("oVirt VM %s has unknown display type: %d"), vm_name, type); + g_debug(error->message); goto error; } @@ -896,9 +923,11 @@ create_ovirt_session(VirtViewerApp *app, const char *uri) virt_viewer_app_set_connect_info(app, NULL, ghost, gport, gtlsport, session_type, NULL, NULL, 0, NULL); - if (virt_viewer_app_create_session(app, session_type) < 0) + if (virt_viewer_app_create_session(app, session_type) < 0) { + g_set_error(&error, VIRT_VIEWER_ERROR, VIRT_VIEWER_ERROR_FAILED, + _("Failed to create a session type %s"), session_type); goto error; - + } #ifdef HAVE_SPICE_GTK if (type == OVIRT_VM_DISPLAY_SPICE) { SpiceSession *session; @@ -930,9 +959,10 @@ error: g_free(gtlsport); g_free(ghost); g_free(host_subject); + g_free(guid); if (error != NULL) - g_error_free(error); + g_propagate_error(err, error); if (display != NULL) g_object_unref(display); if (vm != NULL) @@ -1102,6 +1132,46 @@ connect_dialog(gchar **uri) return retval; } + +#ifdef HAVE_OVIRT +static OvirtVm * +choose_vm(char **vm_name, OvirtCollection *vms_collection, GError **error) +{ + GtkListStore *model; + GtkTreeIter iter; + GHashTable *vms; + GHashTableIter vms_iter; + OvirtVmState state; + OvirtVm *vm; + + g_return_val_if_fail(vm_name != NULL, NULL); + if (*vm_name != NULL) { + free(*vm_name); + } + + model = gtk_list_store_new(1, G_TYPE_STRING); + + vms = ovirt_collection_get_resources(vms_collection); + g_hash_table_iter_init(&vms_iter, vms); + while (g_hash_table_iter_next(&vms_iter, (gpointer *) vm_name, (gpointer *) &vm)) { + g_object_get(G_OBJECT(vm), "state", &state, NULL); + if (state == OVIRT_VM_STATE_UP) { + gtk_list_store_append(model, &iter); + gtk_list_store_set(model, &iter, 0, *vm_name, -1); + } + } + + *vm_name = virt_viewer_vm_connection_choose_name_dialog(GTK_TREE_MODEL(model), error); + g_object_unref(model); + if (*vm_name == NULL) + return NULL; + + vm = OVIRT_VM(ovirt_collection_lookup_resource(vms_collection, *vm_name)); + + return vm; +} +#endif + static gboolean remote_viewer_start(VirtViewerApp *app) { @@ -1168,8 +1238,19 @@ retry_dialog: } #ifdef HAVE_OVIRT if (g_strcmp0(type, "ovirt") == 0) { - if (!create_ovirt_session(app, guri)) { - virt_viewer_app_simple_message_dialog(app, _("Couldn't open oVirt session")); + if (!create_ovirt_session(app, guri, &error)) { + if (error) { + if (!g_error_matches(error, + VIRT_VIEWER_ERROR, + VIRT_VIEWER_VM_CHOOSE_DIALOG_CANCELLED)) { + virt_viewer_app_simple_message_dialog(app, + _("Couldn't open oVirt session: %s"), + error->message); + } + } else { + virt_viewer_app_simple_message_dialog(app, _("Couldn't open oVirt session")); + } + g_clear_error(&error); goto cleanup; } } else diff --git a/src/virt-viewer-util.h b/src/virt-viewer-util.h index 287f9bd..dda7b1d 100644 --- a/src/virt-viewer-util.h +++ b/src/virt-viewer-util.h @@ -30,6 +30,7 @@ extern gboolean doDebug; enum { VIRT_VIEWER_ERROR_FAILED, + VIRT_VIEWER_VM_CHOOSE_DIALOG_CANCELLED, }; #define VIRT_VIEWER_ERROR virt_viewer_error_quark () diff --git a/src/virt-viewer-vm-connection.c b/src/virt-viewer-vm-connection.c new file mode 100644 index 0000000..f6fcd03 --- /dev/null +++ b/src/virt-viewer-vm-connection.c @@ -0,0 +1,105 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2014 Red Hat, Inc. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include +#include + +#include "virt-viewer-vm-connection.h" +#include "virt-viewer-util.h" + +static void +treeview_row_activated_cb(GtkTreeView *treeview G_GNUC_UNUSED, + GtkTreePath *path G_GNUC_UNUSED, + GtkTreeViewColumn *col G_GNUC_UNUSED, + gpointer userdata) +{ + gtk_widget_activate(GTK_WIDGET(userdata)); +} + +static void +treeselection_changed_cb(GtkTreeSelection *selection, gpointer userdata) +{ + gtk_widget_set_sensitive(GTK_WIDGET(userdata), + gtk_tree_selection_count_selected_rows(selection) == 1); +} + +gchar* +virt_viewer_vm_connection_choose_name_dialog(GtkTreeModel *model, GError **error) +{ + GtkBuilder *vm_connection; + GtkWidget *dialog; + GtkButton *button_connect; + GtkTreeView *treeview; + GtkTreeSelection *select; + GtkTreeIter iter; + int dialog_response; + gchar *vm_name = NULL; + + g_return_val_if_fail(model != NULL, NULL); + + if (!gtk_tree_model_get_iter_first(model, &iter)) { + g_set_error_literal(error, + VIRT_VIEWER_ERROR, VIRT_VIEWER_ERROR_FAILED, + _("No virtual machine found")); + return NULL; + } + + vm_connection = virt_viewer_util_load_ui("virt-viewer-vm-connection.xml"); + g_return_val_if_fail(vm_connection != NULL, NULL); + + dialog = GTK_WIDGET(gtk_builder_get_object(vm_connection, "vm-connection-dialog")); + button_connect = GTK_BUTTON(gtk_builder_get_object(vm_connection, "button-connect")); + treeview = GTK_TREE_VIEW(gtk_builder_get_object(vm_connection, "treeview")); + select = GTK_TREE_SELECTION(gtk_builder_get_object(vm_connection, "treeview-selection")); + gtk_tree_view_set_model(treeview, model); + + g_signal_connect(treeview, "row-activated", + G_CALLBACK(treeview_row_activated_cb), button_connect); + g_signal_connect(select, "changed", + G_CALLBACK(treeselection_changed_cb), button_connect); + + gtk_widget_show_all(dialog); + dialog_response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_hide(dialog); + + if (dialog_response == GTK_RESPONSE_ACCEPT && + gtk_tree_selection_get_selected(select, &model, &iter)) { + gtk_tree_model_get(model, &iter, 0, &vm_name, -1); + } else { + g_set_error_literal(error, + VIRT_VIEWER_ERROR, VIRT_VIEWER_VM_CHOOSE_DIALOG_CANCELLED, + _("No virtual machine was chosen")); + } + + gtk_widget_destroy(dialog); + g_object_unref(G_OBJECT(vm_connection)); + + return vm_name; +} + +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/virt-viewer-vm-connection.h b/src/virt-viewer-vm-connection.h new file mode 100644 index 0000000..d198c89 --- /dev/null +++ b/src/virt-viewer-vm-connection.h @@ -0,0 +1,36 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2014 Red Hat, Inc. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef VIRT_VIEWER_VM_CONNECTION_H +#define VIRT_VIEWER_VM_CONNECTION_H + +#include +#include + +gchar* virt_viewer_vm_connection_choose_name_dialog(GtkTreeModel *model, GError **error); + +#endif +/* + * Local variables: + * c-indent-level: 4 + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/virt-viewer-vm-connection.xml b/src/virt-viewer-vm-connection.xml new file mode 100644 index 0000000..04797fa --- /dev/null +++ b/src/virt-viewer-vm-connection.xml @@ -0,0 +1,119 @@ + + + + + False + 5 + Choose a virtual machine + True + center-on-parent + 200 + True + dialog + True + True + + + False + vertical + 6 + + + False + end + + + gtk-cancel + True + True + True + True + + + False + True + 0 + + + + + gtk-connect + True + True + True + True + True + True + + + False + True + 1 + + + + + False + True + end + 0 + + + + + True + True + False + 0 + horizontal + + + + + + Name + + + + 0 + + + + + + + True + True + end + 1 + + + + + True + False + 0 + 0 + 4 + Available virtual machines + end + 26 + + + + + + False + True + end + 2 + + + + + + button-cancel + button-connect + + +