[libvirt] [v2 0/3] virt-manager USB improvements

Hi, This is an update of the patch series sent earlier. v2 adds support for USB redirection over Spice channel. Marc-André Lureau (3): virt-manager: Add controller model in details virt-manager: Add redirected devices details virt-manager: Learn to add USB redirection devices src/virtManager/addhardware.py | 66 ++++++++++- src/virtManager/details.py | 33 +++++- src/virtManager/uihelpers.py | 19 +++ src/vmm-add-hardware.glade | 254 +++++++++++++++++++++++++++++++++++++++- src/vmm-details.glade | 118 +++++++++++++++++++ 5 files changed, 482 insertions(+), 8 deletions(-) -- 1.7.6

--- src/virtManager/details.py | 5 +++++ src/vmm-details.glade | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 0 deletions(-) diff --git a/src/virtManager/details.py b/src/virtManager/details.py index 1fb7d3a..fd6cc3a 100644 --- a/src/virtManager/details.py +++ b/src/virtManager/details.py @@ -3019,7 +3019,12 @@ class vmmDetails(vmmGObjectUI): return type_label = virtinst.VirtualController.pretty_type(dev.type) + model_label = dev.model + if not model_label: + model_label = _("Default") + self.widget("controller-type").set_text(type_label) + self.widget("controller-model").set_text(model_label) def refresh_filesystem_page(self): dev = self.get_hw_selection(HW_LIST_COL_DEVICE) diff --git a/src/vmm-details.glade b/src/vmm-details.glade index 0e2d631..8ab386b 100644 --- a/src/vmm-details.glade +++ b/src/vmm-details.glade @@ -5553,6 +5553,7 @@ I/O:</property> <widget class="GtkTable" id="table14"> <property name="visible">True</property> <property name="n_columns">2</property> + <property name="n_rows">2</property> <property name="column_spacing">6</property> <property name="row_spacing">6</property> <child> @@ -5577,6 +5578,34 @@ I/O:</property> <property name="y_options">GTK_FILL</property> </packing> </child> + <child> + <widget class="GtkLabel" id="mlabel48"> + <property name="visible">True</property> + <property name="label" translatable="yes">Model:</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="controller-model"> + <property name="visible">True</property> + <property name="label">foo</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> </widget> </child> </widget> -- 1.7.6

--- src/virtManager/details.py | 28 ++++++++++++- src/virtManager/uihelpers.py | 19 +++++++++ src/vmm-details.glade | 89 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/virtManager/details.py b/src/virtManager/details.py index fd6cc3a..1ee17ff 100644 --- a/src/virtManager/details.py +++ b/src/virtManager/details.py @@ -186,6 +186,7 @@ def build_hostdev_label(hostdev): addrslt = hostdev.slot addrfun = hostdev.function addrdom = hostdev.domain + redirection = hostdev.redirection def dehex(val): if val.startswith("0x"): @@ -195,7 +196,17 @@ def build_hostdev_label(hostdev): hwlabel = typ.upper() srclabel = typ.upper() - if vendor and product: + if redirection: + if redirection == 'spicevmc': + srclabel += _(" redirected to SPICE client") + elif hostdev.host and hostdev.service: + srclabel += _(" redirected to %s:%s") % (hostdev.host, hostdev.service) + else: + raise RuntimeError("unhandled redirection kind: %s" % redirection) + + hwlabel += _(" redirected") + + elif vendor and product: # USB by vendor + product devstr = " %s:%s" % (dehex(vendor), dehex(product)) srclabel += devstr @@ -913,6 +924,10 @@ class vmmDetails(vmmGObjectUI): sc_mode = self.widget("smartcard-mode-combo") uihelpers.build_smartcard_mode_combo(self.vm, sc_mode) + # Hostdev/redir mode + combo = self.widget("hostdev-mode-combo") + uihelpers.build_usbredir_mode_combo(self.vm, combo) + # Helper function to handle the combo/label pattern used for # video model, sound model, network model, etc. def set_combo_label(self, prefix, value, model_idx=0, label="", @@ -2967,6 +2982,7 @@ class vmmDetails(vmmGObjectUI): return devtype = hostdev.type + redirection = hostdev.redirection pretty_name = None nodedev = lookup_nodedev(self.vm.conn, hostdev) if nodedev: @@ -2975,10 +2991,18 @@ class vmmDetails(vmmGObjectUI): if not pretty_name: pretty_name = build_hostdev_label(hostdev)[0] or "-" - devlabel = "<b>Physical %s Device</b>" % devtype.upper() + if hostdev.redirection: + devlabel = "<b>Redirected %s Device</b>" % devtype.upper() + else: + devlabel = "<b>Physical %s Device</b>" % devtype.upper() self.widget("hostdev-title").set_markup(devlabel) self.widget("hostdev-source").set_text(pretty_name) + self.set_combo_label("hostdev-mode", redirection) + # TODO: let's not make it configurable for now + self.widget("hostdev-mode-title").hide() + self.widget("hostdev-mode-box").hide() + def refresh_video_page(self): vid = self.get_hw_selection(HW_LIST_COL_DEVICE) if not vid: diff --git a/src/virtManager/uihelpers.py b/src/virtManager/uihelpers.py index 82f1f27..af2916f 100644 --- a/src/virtManager/uihelpers.py +++ b/src/virtManager/uihelpers.py @@ -270,6 +270,25 @@ def populate_smartcard_mode_combo(vm, combo): # TODO # model.append(["host-certificates", "Host Certificates"]) +def build_usbredir_mode_combo(vm, combo): + source_mode = gtk.ListStore(str, str, bool) + combo.set_model(source_mode) + text = gtk.CellRendererText() + combo.pack_start(text, True) + combo.add_attribute(text, 'text', 1) + + populate_usbredir_mode_combo(vm, combo) + combo.set_active(0) + +def populate_usbredir_mode_combo(vm, combo): + ignore = vm + model = combo.get_model() + model.clear() + + # [xml value, label, conn details] + model.append(["spicevmc", "Spice channel", False]) + model.append(["tcp", "TCP", True]) + def build_netmodel_combo(vm, combo): dev_model = gtk.ListStore(str, str) combo.set_model(dev_model) diff --git a/src/vmm-details.glade b/src/vmm-details.glade index 8ab386b..62b741b 100644 --- a/src/vmm-details.glade +++ b/src/vmm-details.glade @@ -5170,6 +5170,7 @@ I/O:</property> <child> <widget class="GtkTable" id="table50"> <property name="visible">True</property> + <property name="n_rows">2</property> <property name="n_columns">2</property> <property name="column_spacing">8</property> <property name="row_spacing">4</property> @@ -5180,6 +5181,8 @@ I/O:</property> <property name="label" translatable="yes">Device:</property> </widget> <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> @@ -5192,12 +5195,98 @@ I/O:</property> <property name="selectable">True</property> </widget> <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> <property name="left_attach">1</property> <property name="right_attach">2</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> </child> + + <child> + <widget class="GtkLabel" id="hostdev-mode-title"> + <property name="visible">True</property> + <property name="label" translatable="yes">Mode:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hostdev-mode-box"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkComboBox" id="hostdev-mode-combo"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + <signal name="changed" handler="on_gfx_type_combo_changed"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="hostdev-mode-label"> + <property name="visible">True</property> + <property name="label" translatable="yes">label</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + </widget> </child> </widget> -- 1.7.6

--- src/virtManager/addhardware.py | 66 ++++++++++- src/vmm-add-hardware.glade | 254 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 314 insertions(+), 6 deletions(-) diff --git a/src/virtManager/addhardware.py b/src/virtManager/addhardware.py index b846170..b32c80d 100644 --- a/src/virtManager/addhardware.py +++ b/src/virtManager/addhardware.py @@ -26,7 +26,8 @@ import gtk import virtinst from virtinst import (VirtualCharDevice, VirtualDevice, VirtualVideoDevice, VirtualWatchdog, - VirtualFilesystem, VirtualSmartCardDevice) + VirtualFilesystem, VirtualSmartCardDevice, + VirtualHostDeviceUSBRedir) import virtManager.util as util import virtManager.uihelpers as uihelpers @@ -47,6 +48,7 @@ PAGE_VIDEO = 8 PAGE_WATCHDOG = 9 PAGE_FILESYSTEM = 10 PAGE_SMARTCARD = 11 +PAGE_USBREDIR = 12 char_widget_mappings = { "source_path" : "char-path", @@ -94,6 +96,8 @@ class vmmAddHardware(vmmGObjectUI): "on_fs_type_combo_changed": self.change_fs_type, "on_fs_source_browse_clicked": self.browse_fs_source, + "on_usbredir_type_changed": self.change_usbredir_type, + # Char dev info signals "char_device_type_focus": (self.update_doc, "char_type"), "char_path_focus_in": (self.update_doc, "source_path"), @@ -329,6 +333,10 @@ class vmmAddHardware(vmmGObjectUI): combo = self.widget("smartcard-mode") uihelpers.build_smartcard_mode_combo(self.vm, combo) + # Usbredir widgets + combo = self.widget("usbredir-list") + uihelpers.build_usbredir_mode_combo(self.vm, combo) + # Available HW options is_local = not self.conn.is_remote() is_storage_capable = self.conn.is_storage_capable() @@ -393,6 +401,8 @@ class vmmAddHardware(vmmGObjectUI): "combination.")) add_hw_option("Smartcard", "device_serial", PAGE_SMARTCARD, True, None) + add_hw_option("USB Redirection", "device_usb", PAGE_USBREDIR, + True, None) def reset_state(self): # Storage init @@ -727,6 +737,26 @@ class vmmAddHardware(vmmGObjectUI): modestr = mode.get_model().get_value(mode.get_active_iter(), 0) return modestr + # USB redir getters + def get_config_usbredir_host(self): + host = self.widget("usbredir-host") + if not host.is_sensitive(): + return None + + hoststr = host.get_text() + return hoststr + + def get_config_usbredir_service(self): + service = self.widget("usbredir-service") + if not service.is_sensitive(): + return None + + return int(service.get_value()) + + def get_config_usbredir_mode(self): + modbox = self.widget("usbredir-list") + return modbox.get_model()[modbox.get_active()][0] + ################ # UI listeners # ################ @@ -912,6 +942,8 @@ class vmmAddHardware(vmmGObjectUI): return _("Filesystem Passthrough") if page == PAGE_SMARTCARD: return _("Smartcard") + if page == PAGE_USBREDIR: + return _("USB Redirection") if page == PAGE_CHAR: return self.get_char_type().capitalize() + " Device" @@ -956,6 +988,16 @@ class vmmAddHardware(vmmGObjectUI): if has_mode and self.widget("char-mode").get_active() == -1: self.widget("char-mode").set_active(0) + def change_usbredir_type(self, src): + idx = src.get_active() + if idx < 0: + return + + devtype = src.get_model()[src.get_active()][0] + hostdetails = src.get_model()[src.get_active()][2] + self.widget("usbredir-host").set_sensitive(hostdetails) + self.widget("usbredir-service").set_sensitive(hostdetails) + # FS listeners def browse_fs_source(self, ignore1): self._browse_file(self.widget("fs-source"), isdir=True) @@ -1083,6 +1125,8 @@ class vmmAddHardware(vmmGObjectUI): return self.validate_page_filesystem() elif page_num == PAGE_SMARTCARD: return self.validate_page_smartcard() + elif page_num == PAGE_USBREDIR: + return self.validate_page_usbredir() def validate_page_storage(self): bus, device = self.get_config_disk_target() @@ -1347,7 +1391,25 @@ class vmmAddHardware(vmmGObjectUI): try: self._dev = VirtualSmartCardDevice(conn, mode) except Exception, e: - return self.err.val_err(_("Video device parameter error"), + return self.err.val_err(_("Smartcard device parameter error"), + str(e)) + + def validate_page_usbredir(self): + conn = self.conn.vmm + mode = self.get_config_usbredir_mode() + host = self.get_config_usbredir_host() + service = self.get_config_usbredir_service() + + print host + print service + try: + self._dev = VirtualHostDeviceUSBRedir(conn, redirection=mode) + if host: + self._dev.host = host + if service: + self._dev.service = service + except Exception, e: + return self.err.val_err(_("USB redirected device parameter error"), str(e)) diff --git a/src/vmm-add-hardware.glade b/src/vmm-add-hardware.glade index 879676f..3d13c49 100644 --- a/src/vmm-add-hardware.glade +++ b/src/vmm-add-hardware.glade @@ -2210,6 +2210,17 @@ access in the guest.</property> </packing> </child> <child> + <widget class="GtkLabel" id="label32"> + <property name="visible">True</property> + <property name="label">fs</property> + </widget> + <packing> + <property name="position">10</property> + <property name="tab_fill">False</property> + <property name="type">tab</property> + </packing> + </child> + <child> <widget class="GtkVBox" id="page5-box1"> <property name="visible">True</property> <property name="border_width">1</property> @@ -2279,16 +2290,251 @@ access in the guest.</property> </child> </widget> <packing> - <property name="position">5</property> + <property name="position">11</property> </packing> </child> <child> - <widget class="GtkLabel" id="label32"> + <widget class="GtkLabel" id="label3212"> <property name="visible">True</property> - <property name="label">fs</property> + <property name="label">sc</property> </widget> <packing> - <property name="position">10</property> + <property name="position">11</property> + <property name="tab_fill">False</property> + <property name="type">tab</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="vbox58"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="label437"> + <property name="visible">True</property> + <property name="label" translatable="yes">Please indicate the parameters of the redirected device.</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment162"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">24</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkTable" id="table39"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">12</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="label438"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Host:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">1</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">usbredir-host</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox68"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkEntry" id="usbredir-host"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">•</property> + <property name="activates_default">False</property> + <signal name="focus_in_event" handler="char_host_focus_in"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label439"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Port:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">usbredir-service</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkSpinButton" id="usbredir-service"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">False</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">0 0 67000 1 10 0</property> + <signal name="focus_in_event" handler="char_host_focus_in"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label3213"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Type:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">1</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">usbredir-list</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="usbredir-list"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + <signal name="changed" handler="on_usbredir_type_changed"/> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="position">12</property> + <property name="tab_expand">False</property> + <property name="tab_fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label436"> + <property name="visible">True</property> + <property name="label">usbr</property> + </widget> + <packing> + <property name="position">12</property> <property name="tab_fill">False</property> <property name="type">tab</property> </packing> -- 1.7.6
participants (1)
-
Marc-André Lureau