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. --- changes: - dialog pops up on empty VM name - UI of the dialog is in separate file src/remote-viewer.c | 88 +++++++++++++++++++++- src/virt-viewer-vm-connection.xml | 153 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 src/virt-viewer-vm-connection.xml diff --git a/src/remote-viewer.c b/src/remote-viewer.c index 5f5fa3d..5f5ea2d 100644 --- a/src/remote-viewer.c +++ b/src/remote-viewer.c @@ -74,6 +74,10 @@ enum { PROP_OPEN_RECENT_DIALOG }; +#ifdef HAVE_OVIRT +static OvirtVm * choose_vm_dialog(char **vm_name, OvirtCollection *vms); +#endif + static gboolean remote_viewer_start(VirtViewerApp *self); #ifdef HAVE_SPICE_GTK static gboolean remote_viewer_activate(VirtViewerApp *self, GError **error); @@ -692,10 +696,13 @@ parse_ovirt_uri(const gchar *uri_str, char **rest_uri, char **name, char **usern } if (uri->path == NULL) { + uri->path = g_strdup("/"); + } + + 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); @@ -840,7 +847,10 @@ create_ovirt_session(VirtViewerApp *app, const char *uri) goto error; } vm = OVIRT_VM(ovirt_collection_lookup_resource(vms, vm_name)); - g_return_val_if_fail(vm != NULL, FALSE); + if (vm == NULL && (vm = choose_vm_dialog(&vm_name, vms)) == NULL) { + g_debug("no oVirt VM was chosen"); + 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); @@ -980,6 +990,20 @@ recent_item_activated_dialog_cb(GtkRecentChooser *chooser G_GNUC_UNUSED, gpointe gtk_dialog_response(GTK_DIALOG (data), GTK_RESPONSE_ACCEPT); } +static void +vm_list_selection_changed_dialog_cb(GtkTreeSelection *selection, gpointer data) +{ + GtkTreeIter iter; + GtkTreeModel *model; + const gchar *vm_name; + GtkWidget *entry = data; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) { + gtk_tree_model_get(model, &iter, 0, &vm_name, -1); + gtk_entry_set_text(GTK_ENTRY(entry), vm_name); + } +} + static void make_label_light(GtkLabel* label) { PangoAttrList* attributes = pango_attr_list_new(); @@ -1092,6 +1116,66 @@ connect_dialog(gchar **uri) return retval; } + +#ifdef HAVE_OVIRT +static OvirtVm *choose_vm_dialog(char **vm_name, OvirtCollection *vms_collection) +{ + GtkBuilder *vm_connection; + GtkWidget *dialog, *entry; + GtkTreeModel *store; + GtkTreeSelection *select; + GtkTreeIter iter; + GHashTable *vms; + GHashTableIter vms_iter; + OvirtVm *vm; + char *tmp_vm_name; + OvirtVmState state; + gint vms_running; + + vms = ovirt_collection_get_resources(vms_collection); + g_return_val_if_fail(g_hash_table_size(vms) > 0, NULL); + + vm_connection = virt_viewer_util_load_ui("virt-viewer-vm-connection.xml"); + dialog = GTK_WIDGET(gtk_builder_get_object(vm_connection, "vm-connection-dialog")); + entry = GTK_WIDGET(gtk_builder_get_object(vm_connection, "entry")); + select = GTK_TREE_SELECTION(gtk_builder_get_object(vm_connection, "treeview-selection")); + store = GTK_TREE_MODEL(gtk_builder_get_object(vm_connection, "store")); + + vms_running = 0; + g_hash_table_iter_init(&vms_iter, vms); + while (g_hash_table_iter_next(&vms_iter, (gpointer *) &tmp_vm_name, (gpointer *) &vm)) { + g_object_get(G_OBJECT(vm), "state", &state, NULL); + if (state == OVIRT_VM_STATE_UP) { + gtk_list_store_append(GTK_LIST_STORE(store), &iter); + gtk_list_store_set(GTK_LIST_STORE(store), &iter, 0, tmp_vm_name, -1); + ++vms_running; + } + } + + g_signal_connect(select, "changed", + G_CALLBACK(vm_list_selection_changed_dialog_cb), entry); + if (gtk_tree_model_get_iter_first(store, &iter)) { + gtk_tree_selection_select_iter(select, &iter); + } + /* show and wait for response */ + if (vms_running > 0) { + gtk_widget_show_all(dialog); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + *vm_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + vm = OVIRT_VM(ovirt_collection_lookup_resource(vms_collection, *vm_name)); + } else { + *vm_name = NULL; + vm = NULL; + } + } else { + vm = NULL; + } + gtk_widget_destroy(dialog); + + return vm; +} +#endif + static gboolean remote_viewer_start(VirtViewerApp *app) { diff --git a/src/virt-viewer-vm-connection.xml b/src/virt-viewer-vm-connection.xml new file mode 100644 index 0000000..4c6ced6 --- /dev/null +++ b/src/virt-viewer-vm-connection.xml @@ -0,0 +1,153 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.16.1 --> +<interface> + <object class="GtkListStore" id="store"> + <columns> + <!-- column-name name --> + <column type="gchararray"/> + <!-- column-name state --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkDialog" id="vm-connection-dialog"> + <property name="can_focus">False</property> + <property name="title" translatable="yes">Virtual machine connection details</property> + <property name="modal">True</property> + <property name="destroy_with_parent">True</property> + <property name="type_hint">normal</property> + <child internal-child="vbox"> + <object class="GtkBox" id="dialog-vbox1"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">2</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog-action_area1"> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="button-cancel"> + <property name="label">gtk-cancel</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="button-connect"> + <property name="label">gtk-connect</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_stock">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="n_rows">4</property> + <property name="column_spacing">6</property> + <property name="row_spacing">6</property> + <child> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Virtual machine name</property> + <property name="ellipsize">end</property> + </object> + </child> + <child> + <object class="GtkEntry" id="entry"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="editable">False</property> + <property name="progress_pulse_step">0.16</property> + </object> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Available virtual machines</property> + </object> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + </packing> + </child> + <child> + <object class="GtkTreeView" id="treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="model">store</property> + <property name="headers_visible">False</property> + <property name="enable_grid_lines">horizontal</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeview-selection"/> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn1"> + <property name="title" translatable="yes">Name</property> + <child> + <object class="GtkCellRendererText" id="cellrenderertext1"/> + <attributes> + <attribute name="text">0</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn2"> + <property name="title" translatable="yes">State</property> + <child> + <object class="GtkCellRendererText" id="cellrenderertext2"/> + <attributes> + <attribute name="text">1</attribute> + </attributes> + </child> + </object> + </child> + </object> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + </child> + <action-widgets> + <action-widget response="-6">button-cancel</action-widget> + <action-widget response="-3">button-connect</action-widget> + </action-widgets> + </object> +</interface> -- 1.9.3 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list