[libvirt] [v2 0/4] python-virtinst USB improvements

Hi, Here are a few patches that go with the libvirt series to improve USB support. It adds --controller and --usbredir, as well as providing some new API for virt-manager. The last patch default USB controller to ich9/usb2 on Windows 7 guest. More OS probably support it, since it's been around since early 2007. v2 adds support for USB redirection over Spice. Marc-André Lureau (4): Fix typo s/type/managed Add advanced --controller support, augmenting VirtualController Add --usbredir device RFC: Default to ICH9 USB2 controller for Win7 man/en/virt-clone.1 | 6 ++- man/en/virt-image.1 | 45 ++++++------- man/en/virt-install.1 | 51 +++++++++++++++- man/en/virt-install.pod.in | 59 +++++++++++++++++- tests/cli-test-xml/compare/many-devices.xml | 30 +++++++++ tests/clitest.py | 52 ++++++++++++++++ tests/utils.py | 8 +- tests/xmlparse-xml/change-controllers-in.xml | 4 + tests/xmlparse-xml/change-controllers-out.xml | 4 + tests/xmlparse.py | 9 +++ virt-install | 2 + virtinst/Guest.py | 26 ++++++++ virtinst/VirtualController.py | 78 ++++++++++++++++++++++-- virtinst/VirtualDevice.py | 49 ++++++++++++++- virtinst/VirtualHostDevice.py | 72 +++++++++++++++++++++- virtinst/XMLBuilderDomain.py | 10 +++ virtinst/__init__.py | 5 +- virtinst/cli.py | 82 +++++++++++++++++++++++++ virtinst/osdict.py | 3 + virtinst/support.py | 6 ++ 20 files changed, 557 insertions(+), 44 deletions(-) -- 1.7.6

--- virtinst/VirtualHostDevice.py | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/virtinst/VirtualHostDevice.py b/virtinst/VirtualHostDevice.py index d0c1544..2175266 100644 --- a/virtinst/VirtualHostDevice.py +++ b/virtinst/VirtualHostDevice.py @@ -120,7 +120,7 @@ class VirtualHostDevice(VirtualDevice.VirtualDevice): return self._managed def set_managed(self, val): self._managed = bool(val) - managed = _xml_property(get_type, set_type, + managed = _xml_property(get_managed, set_managed, get_converter=lambda s, x: bool(x == "yes"), set_converter=lambda s, x: x and "yes" or "no", xpath="./@managed") -- 1.7.6

This allow support for USB companion controllers and such. See man/en/virt-install.pod.in doc. We may want to add too a simpler --controller ich9-with-companions, or just --controller usb2. --- man/en/virt-clone.1 | 6 ++- man/en/virt-image.1 | 45 +++++++-------- man/en/virt-install.1 | 33 ++++++++++- man/en/virt-install.pod.in | 39 ++++++++++++- tests/cli-test-xml/compare/many-devices.xml | 30 ++++++++++ tests/clitest.py | 26 ++++++++ tests/utils.py | 8 +- tests/xmlparse-xml/change-controllers-in.xml | 4 + tests/xmlparse-xml/change-controllers-out.xml | 4 + tests/xmlparse.py | 9 +++ virt-install | 1 + virtinst/VirtualController.py | 78 +++++++++++++++++++++++-- virtinst/VirtualDevice.py | 49 +++++++++++++++- virtinst/XMLBuilderDomain.py | 10 +++ virtinst/cli.py | 43 ++++++++++++++ 15 files changed, 344 insertions(+), 41 deletions(-) diff --git a/man/en/virt-clone.1 b/man/en/virt-clone.1 index 9df2fea..20ec173 100644 --- a/man/en/virt-clone.1 +++ b/man/en/virt-clone.1 @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "VIRT-CLONE 1" -.TH VIRT-CLONE 1 "2010-12-17" "" "Virtual Machine Install Tools" +.TH VIRT-CLONE 1 "2011-08-01" "" "Virtual Machine Install Tools" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -232,6 +232,10 @@ guest \s-1XML\s0. .IP "\-\-print\-xml" 2 .IX Item "--print-xml" Print the generated clone \s-1XML\s0 and exit without cloning. +.IP "\-\-replace" 2 +.IX Item "--replace" +Shutdown and remove any existing guest with the passed \f(CW\*(C`\-\-name\*(C'\fR before +cloning the original guest. .IP "\-d, \-\-debug" 2 .IX Item "-d, --debug" Print debugging information to the terminal when running the install process. diff --git a/man/en/virt-image.1 b/man/en/virt-image.1 index 520cc5e..b6ee7b7 100644 --- a/man/en/virt-image.1 +++ b/man/en/virt-image.1 @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "VIRT-IMAGE 1" -.TH VIRT-IMAGE 1 "2011-06-09" "" "Virtual Machine Install Tools" +.TH VIRT-IMAGE 1 "2011-08-01" "" "Virtual Machine Install Tools" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -205,48 +205,49 @@ values. Parameters specific only to fully virtualized guest installs. .IP "\-\-noapic" 2 .IX Item "--noapic" -Disables \s-1APIC\s0 for fully virtualized guest (overrides value in \s-1XML\s0 descriptor) +Force disable \s-1APIC\s0 for the guest. .IP "\-\-noacpi" 2 .IX Item "--noacpi" -Disables \s-1ACPI\s0 for fully virtualized guest (overrides value in \s-1XML\s0 descriptor) +Force disable \s-1ACPI\s0 for the guest. .SS "Networking Configuration" .IX Subsection "Networking Configuration" +.IP "\-w \s-1NETWORK\s0, \-\-network=NETWORK" 2 +.IX Item "-w NETWORK, --network=NETWORK" +Connect the guest to the host network. See \fIvirt\-install\fR\|(1) for details .IP "\-m \s-1MAC\s0, \-\-mac=MAC" 2 .IX Item "-m MAC, --mac=MAC" This is deprecated in favor of \f(CW\*(C`\-\-network ...,mac=MAC,...\*(C'\fR .IP "\-b \s-1BRIDGE\s0, \-\-bridge=BRIDGE" 2 .IX Item "-b BRIDGE, --bridge=BRIDGE" This is deprecated in favor of \f(CW\*(C`\-\-network bridge=BRIDGE\*(C'\fR -.IP "\-w \s-1NETWORK\s0, \-\-network=NETWORK" 2 -.IX Item "-w NETWORK, --network=NETWORK" -Connect the guest to the host network. See \fIvirt\-install\fR\|(1) for details .SS "Graphics Configuration" .IX Subsection "Graphics Configuration" -If no graphics option is specified, \f(CW\*(C`virt\-install\*(C'\fR will default to \-\-vnc -if the \s-1DISPLAY\s0 environment variable is set, otherwise \-\-nographics is used. +If no graphics option is specified, \f(CW\*(C`virt\-image\*(C'\fR will default to +\&'\-\-graphics vnc' if the \s-1DISPLAY\s0 environment variable is set, otherwise +\&'\-\-graphics none' is used. +.IP "\-\-graphics \s-1TYPE\s0,opt1=arg1,opt2=arg2,..." 2 +.IX Item "--graphics TYPE,opt1=arg1,opt2=arg2,..." +Specifies the graphical display configuration. This does not configure any +virtual hardware, just how the guest's graphical display can be accessed. +See \fIvirt\-install\fR\|(1) for details usage info. .IP "\-\-vnc" 2 .IX Item "--vnc" -Setup a virtual console in the guest and export it as a \s-1VNC\s0 server in -the host. See \fIvirt\-install\fR\|(1) for details +This option is deprecated in favor of \f(CW\*(C`\-\-graphics vnc,...\*(C'\fR .IP "\-\-vncport=VNCPORT" 2 .IX Item "--vncport=VNCPORT" -Request a permanent, statically assigned port number for the guest \s-1VNC\s0 -console. See \fIvirt\-install\fR\|(1) for details +This option is deprecated in favor of \f(CW\*(C`\-\-graphics vnc,port=PORT,...\*(C'\fR .IP "\-\-vnclisten=VNCLISTEN" 2 .IX Item "--vnclisten=VNCLISTEN" -Address to listen on for \s-1VNC\s0 connections. See \fIvirt\-install\fR\|(1) for details. +This option is deprecated in favor of \f(CW\*(C`\-\-graphics vnc,listen=LISTEN,...\*(C'\fR .IP "\-k \s-1KEYMAP\s0, \-\-keymap=KEYMAP" 2 .IX Item "-k KEYMAP, --keymap=KEYMAP" -Request that the virtual \s-1VNC\s0 console be configured to run with a non-English -keyboard layout. +This option is deprecated in favor of \f(CW\*(C`\-\-graphics vnc,keymap=KEYMAP,...\*(C'\fR .IP "\-\-sdl" 2 .IX Item "--sdl" -Setup a virtual console in the guest and display an \s-1SDL\s0 window in the -host to render the output. See \fIvirt\-install\fR\|(1) for details +This option is deprecated in favor of \f(CW\*(C`\-\-graphics sdl,...\*(C'\fR .IP "\-\-nographics" 2 .IX Item "--nographics" -Do not attach a graphical device to the guest. See -\&\fIvirt\-install\fR\|(1) for details +This option is deprecated in favor of \f(CW\*(C`\-\-graphics none\*(C'\fR .SS "Miscellaneous Options" .IX Subsection "Miscellaneous Options" .IP "\-p, \-\-print" 2 @@ -272,11 +273,7 @@ Do not check disk images against checksums (if they are listed in the image xml). .IP "\-d, \-\-debug" 2 .IX Item "-d, --debug" -Print debugging information -.IP "\-\-force" 2 -.IX Item "--force" -Prevent interactive prompts. If the intended prompt was a yes/no prompt, always -say yes. For any other prompts, the application will exit. +Print debugging information. .SH "EXAMPLES" .IX Header "EXAMPLES" Create and start a guest called \f(CW\*(C`example\*(C'\fR with a \s-1VNC\s0 console from diff --git a/man/en/virt-install.1 b/man/en/virt-install.1 index 5a2e244..5f15e8c 100644 --- a/man/en/virt-install.1 +++ b/man/en/virt-install.1 @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "VIRT-INSTALL 1" -.TH VIRT-INSTALL 1 "2011-07-26" "" "Virtual Machine Install Tools" +.TH VIRT-INSTALL 1 "2011-08-29" "" "Virtual Machine Install Tools" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -847,7 +847,7 @@ completeness). .IP "\-\-virt\-type" 2 .IX Item "--virt-type" The hypervisor to install on. Example choices are kvm, qemu, xen, or kqemu. -Availabile options are listed via 'virsh capabilities' in the <domain> tags. +Available options are listed via 'virsh capabilities' in the <domain> tags. .IP "\-\-accelerate" 2 .IX Item "--accelerate" Prefer \s-1KVM\s0 or \s-1KQEMU\s0 (in that order) if installing a \s-1QEMU\s0 guest. This behavior @@ -861,6 +861,35 @@ Force disable \s-1APIC\s0 for the guest. Force disable \s-1ACPI\s0 for the guest. .SS "Device Options" .IX Subsection "Device Options" +.IP "\-\-controller=TYPE[,OPTS]" 2 +.IX Item "--controller=TYPE[,OPTS]" +Attach a controller device to the guest. \s-1TYPE\s0 is one of: +\&\fBide\fR, \fBfdc\fR, \fBscsi\fR, \fBsata\fR, \fBvirtio-serial\fR, or \fBusb\fR. +.RS 2 +.IP "\fBmodel\fR" 4 +.IX Item "model" +Controller model. +.IP "\fBaddress\fR" 4 +.IX Item "address" +Controller address, current \s-1PCI\s0 of form 'bus:domain:slot:function'. +.IP "\fBindex\fR" 4 +.IX Item "index" +A decimal integer describing in which order the bus controller is +encountered, and to reference the controller bus. +.IP "\fBmaster\fR" 4 +.IX Item "master" +Applicable to \s-1USB\s0 companion controllers, to define the master bus startport. +.RE +.RS 2 +.Sp +Example: +.IP "\fB\-\-controller usb,model=ich9\-uhci2,address=0:0:4.7,index=0,master=2\fR" 4 +.IX Item "--controller usb,model=ich9-uhci2,address=0:0:4.7,index=0,master=2" +Adds a \s-1ICH9\s0 \s-1USB\s0 companion controller on \s-1PCI\s0 address 0:0:4.7 with +master bus 0 and first port 2. +.RE +.RS 2 +.RE .IP "\-\-host\-device=HOSTDEV" 2 .IX Item "--host-device=HOSTDEV" Attach a physical host device to the guest. Some example values for \s-1HOSTDEV:\s0 diff --git a/man/en/virt-install.pod.in b/man/en/virt-install.pod.in index 629b906..392137f 100644 --- a/man/en/virt-install.pod.in +++ b/man/en/virt-install.pod.in @@ -797,7 +797,7 @@ completeness). =item --virt-type The hypervisor to install on. Example choices are kvm, qemu, xen, or kqemu. -Availabile options are listed via 'virsh capabilities' in the <domain> tags. +Available options are listed via 'virsh capabilities' in the <domain> tags. =item --accelerate @@ -823,6 +823,43 @@ Force disable ACPI for the guest. =over 2 +=item --controller=TYPE[,OPTS] + +Attach a controller device to the guest. TYPE is one of: +B<ide>, B<fdc>, B<scsi>, B<sata>, B<virtio-serial>, or B<usb>. + +=over 4 + +=item B<model> + +Controller model. + +=item B<address> + +Controller address, current PCI of form 'bus:domain:slot:function'. + +=item B<index> + +A decimal integer describing in which order the bus controller is +encountered, and to reference the controller bus. + +=item B<master> + +Applicable to USB companion controllers, to define the master bus startport. + +=back + +Example: + +=over 4 + +=item B<--controller usb,model=ich9-uhci2,address=0:0:4.7,index=0,master=2> + +Adds a ICH9 USB companion controller on PCI address 0:0:4.7 with +master bus 0 and first port 2. + +=back + =item --host-device=HOSTDEV Attach a physical host device to the guest. Some example values for HOSTDEV: diff --git a/tests/cli-test-xml/compare/many-devices.xml b/tests/cli-test-xml/compare/many-devices.xml index c37f189..6b4995d 100644 --- a/tests/cli-test-xml/compare/many-devices.xml +++ b/tests/cli-test-xml/compare/many-devices.xml @@ -38,6 +38,21 @@ <target dev='hdc' bus='ide'/> <readonly/> </disk> + <controller type='usb' index='0' model='ich9-ehci1'> + <address type='pci' domain='0' bus='0' slot='4' function='7'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci1'> + <master startport='0'/> + <address type='pci' domain='0' bus='0' slot='4' function='0'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci2'> + <master startport='2'/> + <address type='pci' domain='0' bus='0' slot='4' function='1'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci3'> + <master startport='4'/> + <address type='pci' domain='0' bus='0' slot='4' function='2'/> + </controller> <filesystem accessmode='squash'> <source dir='/source'/> <target dir='/target'/> @@ -104,6 +119,21 @@ <target dev='hdc' bus='ide'/> <readonly/> </disk> + <controller type='usb' index='0' model='ich9-ehci1'> + <address type='pci' domain='0' bus='0' slot='4' function='7'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci1'> + <master startport='0'/> + <address type='pci' domain='0' bus='0' slot='4' function='0'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci2'> + <master startport='2'/> + <address type='pci' domain='0' bus='0' slot='4' function='1'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci3'> + <master startport='4'/> + <address type='pci' domain='0' bus='0' slot='4' function='2'/> + </controller> <filesystem accessmode='squash'> <source dir='/source'/> <target dir='/target'/> diff --git a/tests/clitest.py b/tests/clitest.py index 7f332dc..c0f9294 100644 --- a/tests/clitest.py +++ b/tests/clitest.py @@ -545,6 +545,10 @@ args_dict = { ("--hvm --cdrom %(EXISTIMG2)s --file %(EXISTIMG1)s --os-variant win2k3 --wait 0 --vcpus cores=4", "w2k3-cdrom"), # Lot's of devices ("--hvm --pxe " + "--controller usb,model=ich9-ehci1,address=0:0:4.7,index=0 " + "--controller usb,model=ich9-uhci1,address=0:0:4.0,index=0,master=0 " + "--controller usb,model=ich9-uhci2,address=0:0:4.1,index=0,master=2 " + "--controller usb,model=ich9-uhci3,address=0:0:4.2,index=0,master=4 " "--disk %(EXISTIMG1)s,cache=writeback,io=threads,perms=sh,serial=WD-WMAP9A966149 " "--disk %(NEWIMG1)s,sparse=false,size=.001,perms=ro,error_policy=enospace " "--disk device=cdrom " @@ -599,6 +603,28 @@ args_dict = { }, # category "network" + "controller": { + "args": "--noautoconsole --nodisks --pxe", + + "valid": [ + "--controller usb,model=ich9-ehci1,address=0:0:4.7", + "--controller usb,model=ich9-ehci1,address=0:0:4.7,index=0", + "--controller usb,model=ich9-ehci1,address=0:0:4.7,index=1,master=0", + ], + + "invalid": [ + # Missing argument + "--controller", + # Invalid argument + "--controller foo", + # Invalid values + "--controller usb,model=ich9-ehci1,address=0:0:4.7,index=bar,master=foo", + # --bogus + "--controller host,foobar=baz", + ], + + }, # category "controller" + "hostdev" : { "args": "--noautoconsole --nographics --nodisks --pxe", diff --git a/tests/utils.py b/tests/utils.py index 1a542e5..1cf5fec 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -96,10 +96,10 @@ def sanitize_xml_for_define(xml): def test_create(testconn, xml): xml = sanitize_xml_for_define(xml) - try: - dom = testconn.defineXML(xml) - except Exception, e: - raise RuntimeError(str(e) + "\n" + xml) +# try: + dom = testconn.defineXML(xml) +# except Exception, e: +# raise RuntimeError(str(e) + "\n" + xml) try: dom.create() diff --git a/tests/xmlparse-xml/change-controllers-in.xml b/tests/xmlparse-xml/change-controllers-in.xml index 4ea5be6..34de7e1 100644 --- a/tests/xmlparse-xml/change-controllers-in.xml +++ b/tests/xmlparse-xml/change-controllers-in.xml @@ -22,5 +22,9 @@ <controller type="ide" index="3"/> <controller type="virtio-serial" index="0" ports="32" vectors="17"/> <controller type="scsi" index="1"/> + <controller type='usb' index='3' model='ich9-uhci3'> + <master startport='4'/> + <address type='pci' domain='0' bus='0' slot='4' function='2'/> + </controller> </devices> </domain> diff --git a/tests/xmlparse-xml/change-controllers-out.xml b/tests/xmlparse-xml/change-controllers-out.xml index 93f9362..4a26f1a 100644 --- a/tests/xmlparse-xml/change-controllers-out.xml +++ b/tests/xmlparse-xml/change-controllers-out.xml @@ -22,5 +22,9 @@ <controller type="ide" index="1"/> <controller type="virtio-serial" index="7" ports="5"/> <controller type="scsi" index="2"/> + <controller type="usb" index="9" model="ich9-uhci3"> + <master startport="2"/> + <address type="pci" domain="0" bus="0" slot="4" function="2"/> + </controller> </devices> </domain> diff --git a/tests/xmlparse.py b/tests/xmlparse.py index c30a7d3..0ec0ad9 100644 --- a/tests/xmlparse.py +++ b/tests/xmlparse.py @@ -343,6 +343,7 @@ class XMLParseTest(unittest.TestCase): dev1 = guest.get_devices("controller")[0] dev2 = guest.get_devices("controller")[1] dev3 = guest.get_devices("controller")[2] + dev4 = guest.get_devices("controller")[3] check = self._make_checker(dev1) check("type", "ide") @@ -358,6 +359,14 @@ class XMLParseTest(unittest.TestCase): check("type", "scsi") check("index", "1", "2") + check = self._make_checker(dev4) + check("type", "usb") + check("index", "3", "9") + check("model", "ich9-uhci3") + + check = self._make_checker(dev4.get_master()) + check("startport", "4", "2") + self._alter_compare(guest.get_config_xml(), outfile) def testAlterNics(self): diff --git a/virt-install b/virt-install index 6e672e9..837475d 100755 --- a/virt-install +++ b/virt-install @@ -479,6 +479,7 @@ def build_guest_instance(conn, options): # Non-default devices + cli.get_controller(guest, options.controller) if not options.nonetworks: get_networks(guest, options) get_graphics(guest, options) diff --git a/virtinst/VirtualController.py b/virtinst/VirtualController.py index 8e5d19d..d9297ae 100644 --- a/virtinst/VirtualController.py +++ b/virtinst/VirtualController.py @@ -19,7 +19,8 @@ import VirtualDevice #from virtinst import _gettext as _ -from XMLBuilderDomain import _xml_property +from XMLBuilderDomain import XMLBuilderDomain, _xml_property +import logging class VirtualController(VirtualDevice.VirtualDevice): @@ -30,9 +31,10 @@ class VirtualController(VirtualDevice.VirtualDevice): CONTROLLER_TYPE_SCSI = "scsi" CONTROLLER_TYPE_SATA = "sata" CONTROLLER_TYPE_VIRTIOSERIAL = "virtio-serial" + CONTROLLER_TYPE_USB = "usb" CONTROLLER_TYPES = [CONTROLLER_TYPE_IDE, CONTROLLER_TYPE_FDC, CONTROLLER_TYPE_SCSI, CONTROLLER_TYPE_SATA, - CONTROLLER_TYPE_VIRTIOSERIAL] + CONTROLLER_TYPE_VIRTIOSERIAL, CONTROLLER_TYPE_USB] @staticmethod def pretty_type(ctype): @@ -41,7 +43,8 @@ class VirtualController(VirtualDevice.VirtualDevice): VirtualController.CONTROLLER_TYPE_FDC : "Floppy", VirtualController.CONTROLLER_TYPE_SCSI : "SCSI", VirtualController.CONTROLLER_TYPE_SATA : "SATA", - VirtualController.CONTROLLER_TYPE_VIRTIOSERIAL : "Virtio Serial" + VirtualController.CONTROLLER_TYPE_VIRTIOSERIAL : "Virtio Serial", + VirtualController.CONTROLLER_TYPE_USB : "USB" } if ctype not in pretty_mappings: @@ -63,22 +66,42 @@ class VirtualController(VirtualDevice.VirtualDevice): return VirtualControllerSATA elif ctype == VirtualController.CONTROLLER_TYPE_VIRTIOSERIAL: return VirtualControllerVirtioSerial + elif ctype == VirtualController.CONTROLLER_TYPE_USB: + return VirtualControllerUSB _controller_type = None - def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): + def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None, + model=None): VirtualDevice.VirtualDevice.__init__(self, conn, parsexml, parsexmlnode, caps) self._index = 0 self._ports = None self._vectors = None + self._model = None + self._master = VirtualDeviceMaster(conn, + parsexml=parsexml, + parsexmlnode=parsexmlnode, + caps=caps) + + if self._is_parse(): + return + + self.model = model def get_type(self): return self._controller_type type = _xml_property(get_type, xpath="./@type") + def get_model(self): + return self._model + def set_model(self, model): + self._model = model + model = _xml_property(get_model, set_model, + xpath="./@model") + def get_index(self): return self._index def set_index(self, val): @@ -100,6 +123,11 @@ class VirtualController(VirtualDevice.VirtualDevice): ports = _xml_property(get_ports, set_ports, xpath="./@ports") + def set_master(self, masterstr): + self._master.parse_friendly_master(masterstr) + def get_master(self): + return self._master + def _extra_config(self): return "" @@ -107,9 +135,16 @@ class VirtualController(VirtualDevice.VirtualDevice): extra = self._extra_config() xml = " <controller type='%s' index='%s'" % (self.type, self.index) + if self.model: + xml += " model='%s'" % self.model xml += extra - xml += "/>" - + childxml = self.indent(self._master.get_xml_config(), 6) + childxml += self.indent(self.address.get_xml_config(), 6) + if len(childxml) == 0: + return xml + "/>" + xml += ">\n" + xml += childxml + xml += " </controller>" return xml @@ -136,3 +171,34 @@ class VirtualControllerVirtioSerial(VirtualController): xml += " vectors='%s'" % self.vectors return xml + +class VirtualControllerUSB(VirtualController): + _controller_type = VirtualController.CONTROLLER_TYPE_USB + + +class VirtualDeviceMaster(XMLBuilderDomain): + def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): + XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, + caps=caps) + + self._startport = None + + def parse_friendly_master(self, masterstr): + try: + int(masterstr) + self._startport = masterstr + except: + logging.exception("Error parsing device master.") + return None + + def _get_startport(self): + return self._startport + def _set_startport(self, val): + self._startport = val + startport = _xml_property(_get_startport, _set_startport, xpath="./master/@startport") + + def _get_xml_config(self): + if self.startport is None: + return + + return "<master startport='%s'/>" % self.startport diff --git a/virtinst/VirtualDevice.py b/virtinst/VirtualDevice.py index 386eb00..08df6d3 100644 --- a/virtinst/VirtualDevice.py +++ b/virtinst/VirtualDevice.py @@ -21,6 +21,7 @@ from XMLBuilderDomain import XMLBuilderDomain, _xml_property from virtinst import _gettext as _ +import logging class VirtualDevice(XMLBuilderDomain): """ @@ -113,6 +114,10 @@ class VirtualDevice(XMLBuilderDomain): ignore = meter return + def set_address(self, addrstr): + self.address = VirtualDeviceAddress(self.conn, addrstr=addrstr) + + class VirtualDeviceAlias(XMLBuilderDomain): def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, @@ -132,9 +137,15 @@ class VirtualDeviceAlias(XMLBuilderDomain): class VirtualDeviceAddress(XMLBuilderDomain): - TYPES = ["pci", "drive", "virtio-serial", "ccid"] + ADDRESS_TYPE_PCI = "pci" + ADDRESS_TYPE_DRIVE = "drive" + ADDRESS_TYPE_VIRTIO_SERIAL = "virtio-serial" + ADDRESS_TYPE_CCID = "ccid" - def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None): + TYPES = [ADDRESS_TYPE_PCI, ADDRESS_TYPE_DRIVE, + ADDRESS_TYPE_VIRTIO_SERIAL, ADDRESS_TYPE_CCID] + + def __init__(self, conn, parsexml=None, parsexmlnode=None, caps=None, addrstr=None): XMLBuilderDomain.__init__(self, conn, parsexml, parsexmlnode, caps=caps) @@ -160,6 +171,25 @@ class VirtualDeviceAddress(XMLBuilderDomain): # CCID address: # <address type='ccid' controller='0' slot='0'/> + if addrstr: + self.parse_friendly_address(addrstr) + + def parse_friendly_address(self, addrstr): + try: + if addrstr.count(":") in [1, 2] and addrstr.count("."): + self.type = self.ADDRESS_TYPE_PCI + addrstr, self.function = addrstr.split(".", 1) + addrstr, self.slot = addrstr.rsplit(":", 1) + self.domain = "0" + if addrstr.count(":"): + self.domain, self.bus = addrstr.split(":", 1) + else: + raise ValueError(_("Could not determine or unsupported format of '%s'") % addrstr) + except: + logging.exception("Error parsing address.") + return None + + def clear(self): self._type = None self._bus = None @@ -223,4 +253,17 @@ class VirtualDeviceAddress(XMLBuilderDomain): port = _xml_property(_get_port, _set_port, xpath="./address/@port") def _get_xml_config(self): - return "" + if not self.type: + return + + xml = "<address type='%s'" % self.type + if self.type == self.ADDRESS_TYPE_PCI: + xml += " domain='%s' bus='%s' slot='%s' function='%s'" % (self.domain, self.bus, self.slot, self.function) + elif self.type == self.ADDRESS_TYPE_DRIVE: + xml += " controller='%s' bus='%s' unit='%s'" % (self.controller, self.bus, self.unit) + elif self.type == self.ADDRESS_TYPE_VIRTIO_SERIAL: + xml += " controller='%s' bus='%s' port='%s'" % (self.controller, self.bus, self.port) + elif self.type == self.ADDRESS_TYPE_CCID: + xml += " controller='%s' slot='%s'" % (self.controller, self.slot) + xml += "/>" + return xml diff --git a/virtinst/XMLBuilderDomain.py b/virtinst/XMLBuilderDomain.py index 6a489b5..d9b263e 100644 --- a/virtinst/XMLBuilderDomain.py +++ b/virtinst/XMLBuilderDomain.py @@ -495,3 +495,13 @@ class XMLBuilderDomain(object): return _sanitize_libxml_xml(node.serialize()) return self._get_xml_config(*args, **kwargs) + + @staticmethod + def indent(xmlstr, level): + xml = "" + if not xmlstr: + return xml + + for l in iter(xmlstr.splitlines()): + xml += " " * level + l + "\n" + return xml diff --git a/virtinst/cli.py b/virtinst/cli.py index e130e7d..07481dd 100644 --- a/virtinst/cli.py +++ b/virtinst/cli.py @@ -994,6 +994,16 @@ def get_smartcard(guest, sc_opts): if dev: guest.add_device(dev) +def get_controller(guest, sc_opts): + for sc in listify(sc_opts): + try: + dev = parse_controller(guest, sc) + except Exception, e: + fail(_("Error in controller device parameters: %s") % str(e)) + + if dev: + guest.add_device(dev) + ############################# # Common CLI option/group # ############################# @@ -1062,6 +1072,9 @@ def add_net_option(devg): "--network network=mynet,model=virtio,mac=00:11...")) def add_device_options(devg): + devg.add_option("", "--controller", dest="controller", action="append", + help=_("Configure a guest controller device. Ex:\n" + "--controller type=usb,model=ich9-ehci1")) devg.add_option("", "--serial", dest="serials", action="append", help=_("Configure a guest serial device")) devg.add_option("", "--parallel", dest="parallels", action="append", @@ -1680,6 +1693,36 @@ def parse_graphics(guest, optstring, dev=None): return dev ####################### +# --controller parsing # +####################### + +def parse_controller(guest, optstring, dev=None): + if optstring is None: + return None + + # Peel the mode off the front + opts = parse_optstr(optstring, remove_first="type") + ctrltype = get_opt_param(opts, "type") + address = get_opt_param(opts, "address") + master = get_opt_param(opts, "master") + + if not dev: + cl = virtinst.VirtualController.get_class_for_type(ctrltype) + dev = cl(guest.conn, model=opts.get("model")) + + set_param = _build_set_param(dev, opts) + + set_param("model", "model") + set_param("index", "index") + dev.set_address(address) + if master: + dev.set_master(master) + if opts: + raise ValueError(_("Unknown options %s") % opts.keys()) + + return dev + +####################### # --smartcard parsing # ####################### -- 1.7.6

Allows to add a USB redirected device. Via TCP server: --usbredir tcp,server=host:port Or over Spice: --usbredir spicevmc --- man/en/virt-install.1 | 18 ++++++++++ man/en/virt-install.pod.in | 20 ++++++++++++ tests/clitest.py | 26 +++++++++++++++ virt-install | 1 + virtinst/VirtualHostDevice.py | 70 +++++++++++++++++++++++++++++++++++++++++ virtinst/__init__.py | 5 ++- virtinst/cli.py | 39 +++++++++++++++++++++++ 7 files changed, 177 insertions(+), 2 deletions(-) diff --git a/man/en/virt-install.1 b/man/en/virt-install.1 index 5f15e8c..21a5ac1 100644 --- a/man/en/virt-install.1 +++ b/man/en/virt-install.1 @@ -1106,6 +1106,24 @@ to the guest See \f(CW\*(C`http://libvirt.org/formatdomain.html#elementsSmartcard\*(C'\fR for complete details. .RE +.IP "\-\-usbredir=SOURCE[,OPTS]" 2 +.IX Item "--usbredir=SOURCE[,OPTS]" +Add a \s-1USB\s0 redirected device. +.RS 2 +.IP "\fBserver\fR" 4 +.IX Item "server" +The \s-1USB\s0 server connection details, of the form 'server:port'. +.RE +.RS 2 +.Sp +An example invocation: +.IP "\fB\-\-usbredir tcp,server=localhost:4000\fR" 4 +.IX Item "--usbredir tcp,server=localhost:4000" +Add a \s-1USB\s0 redirected device provided by the \s-1TCP\s0 server on 'localhost' +port 4000. +.RE +.RS 2 +.RE .SS "Miscellaneous Options" .IX Subsection "Miscellaneous Options" .IP "\-\-autostart" 2 diff --git a/man/en/virt-install.pod.in b/man/en/virt-install.pod.in index 392137f..f174250 100644 --- a/man/en/virt-install.pod.in +++ b/man/en/virt-install.pod.in @@ -1104,8 +1104,28 @@ to the guest See C<http://libvirt.org/formatdomain.html#elementsSmartcard> for complete details. +=item --usbredir=SOURCE[,OPTS] +Add a USB redirected device. +=over 4 + +=item B<server> + +The USB server connection details, of the form 'server:port'. + +=back + +An example invocation: + +=over 4 + +=item B<--usbredir tcp,server=localhost:4000> + +Add a USB redirected device provided by the TCP server on 'localhost' +port 4000. + +=back =back diff --git a/tests/clitest.py b/tests/clitest.py index c0f9294..d0499a8 100644 --- a/tests/clitest.py +++ b/tests/clitest.py @@ -645,6 +645,32 @@ args_dict = { ], }, # category "hostdev" + "usbredir" : { + "args": "--noautoconsole --nographics --nodisks --pxe", + + "valid" : [ + "--usbredir spicevmc", + "--usbredir tcp,server=localhost:4000", + # Different host server + "--usbredir tcp,server=127.0.0.1:4002", + ], + + "invalid" : [ + # Missing argument + "--usbredir", + # Invalid argument + "--usbredir spicevmc,server=foo:12", + # Missing argument + "--usbredir tcp,server=", + # Invalid address + "--usbredir tcp,server=localhost:p4000", + # Missing address + "--usbredir tcp,server=localhost:", + # Missing host + "--usbredir tcp,server=:399", + ], + }, # category "usbredir" + "remote" : { "args": "--connect %(REMOTEURI)s --nographics --noautoconsole", diff --git a/virt-install b/virt-install index 837475d..6b20efe 100755 --- a/virt-install +++ b/virt-install @@ -480,6 +480,7 @@ def build_guest_instance(conn, options): # Non-default devices cli.get_controller(guest, options.controller) + cli.get_usbredir(guest, options.usbredir) if not options.nonetworks: get_networks(guest, options) get_graphics(guest, options) diff --git a/virtinst/VirtualHostDevice.py b/virtinst/VirtualHostDevice.py index 2175266..dd0604f 100644 --- a/virtinst/VirtualHostDevice.py +++ b/virtinst/VirtualHostDevice.py @@ -93,6 +93,9 @@ class VirtualHostDevice(VirtualDevice.VirtualDevice): self._domain = "0x0" self._slot = None self._function = None + self._host = None + self._service = None + self._redirection = None if self._is_parse(): return @@ -116,6 +119,13 @@ class VirtualHostDevice(VirtualDevice.VirtualDevice): type = _xml_property(get_type, set_type, xpath="./@type") + def get_redirection(self): + return self._redirection + def set_redirection(self, val): + self._redirection = val + redirection = _xml_property(get_redirection, set_redirection, + xpath="./@redirection") + def get_managed(self): return self._managed def set_managed(self, val): @@ -174,6 +184,23 @@ class VirtualHostDevice(VirtualDevice.VirtualDevice): slot = _xml_property(get_slot, set_slot, xpath="./source/address/@slot") + def get_host(self): + return self._host + def set_host(self, val): + if len(val) == 0: + raise ValueError(_("Invalid host value")) + self._host = val + host = _xml_property(get_host, set_host, + xpath="./source/@host") + + def get_service(self): + return self._service + def set_service(self, val): + int(val) + self._service = val + service = _xml_property(get_service, set_service, + xpath="./source/@service") + def _get_source_xml(self): raise NotImplementedError("Must be implemented in subclass") @@ -245,6 +272,49 @@ class VirtualHostDeviceUSB(VirtualHostDevice): # No libvirt api support for USB Detach/Reset yet return + +class VirtualHostDeviceUSBRedir(VirtualHostDevice): + + def __init__(self, conn, nodedev=None, redirection=None, serverstr=None): + VirtualHostDevice.__init__(self, conn, nodedev) + + self.mode = "subsystem" + self.type = "usb" + self.redirection = redirection + if serverstr: + self.parse_friendly_server(serverstr) + + def parse_friendly_server(self, serverstr): + if serverstr.count(":") == 1: + self.host, self.service = serverstr.split(":") + else: + raise ValueError(_("Could not determine or unsupported format of '%s'") % serverstr) + + def _get_xml_config(self): + xml = (" <hostdev mode='%s' type='%s' redirection='%s'" % \ + (self.mode, self.type, self.redirection)) + if self.redirection == 'spicevmc': + xml += "/>" + return xml + xml += ">\n" + xml += (" <source mode='connect' host='%s' service='%s'/>\n" % \ + (self.host, self.service)) + xml += " </hostdev>" + return xml + + def _get_source_xml(self): # unused + return "" + + def setup(self, conn=None): + """ + DEPRECATED: Please use setup_dev instead + """ + if not conn: + conn = self.conn + + # No libvirt api support for USB Detach/Reset yet + return + class VirtualHostDevicePCI(VirtualHostDevice): def __init__(self, conn, nodedev=None): diff --git a/virtinst/__init__.py b/virtinst/__init__.py index d7c328b..3c4ffd3 100644 --- a/virtinst/__init__.py +++ b/virtinst/__init__.py @@ -43,7 +43,7 @@ from VirtualAudio import VirtualAudio from VirtualInputDevice import VirtualInputDevice from VirtualDisk import VirtualDisk, XenDisk from VirtualHostDevice import (VirtualHostDevice, VirtualHostDeviceUSB, - VirtualHostDevicePCI) + VirtualHostDevicePCI, VirtualHostDeviceUSBRedir) from VirtualCharDevice import VirtualCharDevice from VirtualVideoDevice import VirtualVideoDevice from VirtualController import VirtualController @@ -80,4 +80,5 @@ __all__ = ["Guest", "XenGuest", "VirtualNetworkInterface", "VirtualHostDevice", "VirtualHostDeviceUSB", "VirtualVideoDevice", "VirtualHostDevicePCI", "VirtualCharDevice", "VirtualInputDevice", "VirtualController", "VirtualWatchdog", - "VirtualFilesystem", "VirtualSmartCardDevice"] + "VirtualFilesystem", "VirtualSmartCardDevice", + "VirtualHostDeviceUSBRedir"] diff --git a/virtinst/cli.py b/virtinst/cli.py index 07481dd..11b46e5 100644 --- a/virtinst/cli.py +++ b/virtinst/cli.py @@ -1004,6 +1004,16 @@ def get_controller(guest, sc_opts): if dev: guest.add_device(dev) +def get_usbredir(guest, sc_opts): + for sc in listify(sc_opts): + try: + dev = parse_usbredir(guest, sc) + except Exception, e: + fail(_("Error in usbredir device parameters: %s") % str(e)) + + if dev: + guest.add_device(dev) + ############################# # Common CLI option/group # ############################# @@ -1096,6 +1106,9 @@ def add_device_options(devg): devg.add_option("", "--smartcard", dest="smartcard", action="append", help=_("Configure a guest smartcard device. Ex:\n" "--smartcard mode=passthrough")) + devg.add_option("", "--usbredir", dest="usbredir", action="append", + help=_("Configure a guest USB redirection device. Ex:\n" + "--usbredir tcp,server=192.168.1.1:4000")) def add_gfx_option(devg): devg.add_option("", "--graphics", dest="graphics", action="append", @@ -1749,6 +1762,32 @@ def parse_smartcard(guest, optstring, dev=None): return dev ###################### +# --usbredir parsing # +###################### + +def parse_usbredir(guest, optstring, dev=None): + if optstring is None: + return None + + # Peel the mode off the front + opts = parse_optstr(optstring, remove_first="source") + source = get_opt_param(opts, "source") + server = get_opt_param(opts, "server") + + if source == "none": + return None + + if not dev: + dev = virtinst.VirtualHostDeviceUSBRedir(guest.conn, + redirection=source, + serverstr=server) + + if opts: + raise ValueError(_("Unknown options %s") % opts.keys()) + + return dev + +###################### # --watchdog parsing # ###################### -- 1.7.6

We may want to add more guests, though I don't know exactly which version supports it. So this patch is a proof-of-concept/idea. --- virtinst/Guest.py | 26 ++++++++++++++++++++++++++ virtinst/osdict.py | 3 +++ virtinst/support.py | 6 ++++++ 3 files changed, 35 insertions(+), 0 deletions(-) diff --git a/virtinst/Guest.py b/virtinst/Guest.py index dfb5ed2..9d26192 100644 --- a/virtinst/Guest.py +++ b/virtinst/Guest.py @@ -40,6 +40,7 @@ from VirtualDevice import VirtualDevice from VirtualDisk import VirtualDisk from VirtualInputDevice import VirtualInputDevice from VirtualCharDevice import VirtualCharDevice +from VirtualController import VirtualControllerUSB from Clock import Clock from Seclabel import Seclabel from CPU import CPU @@ -1439,6 +1440,7 @@ class Guest(XMLBuilderDomain.XMLBuilderDomain): inputtype = VirtualDevice.VIRTUAL_DEV_INPUT gfxtype = VirtualDevice.VIRTUAL_DEV_GRAPHICS channeltype = VirtualDevice.VIRTUAL_DEV_CHANNEL + ctrltype = VirtualDevice.VIRTUAL_DEV_CONTROLLER # Set default input values input_type = self._lookup_device_param(inputtype, "type") @@ -1462,6 +1464,30 @@ class Guest(XMLBuilderDomain.XMLBuilderDomain): disk.bus = "xen" used_targets.append(disk.generate_target(used_targets)) + def has_usb_ctrl(): + for ctrl in devlist_func(ctrltype): + if ctrl.type == ctrl.CONTROLLER_TYPE_USB: + return True + + if (not has_usb_ctrl() and + self._lookup_osdict_key("usb") == "ich9"): + ctrl = VirtualControllerUSB(self.conn, + model="ich9-ehci1") + self.add_device(ctrl) + ctrl = VirtualControllerUSB(self.conn, + model="ich9-uhci1") + ctrl.get_master().startport = 0 + self.add_device(ctrl) + ctrl = VirtualControllerUSB(self.conn, + model="ich9-uhci2") + ctrl.get_master().startport = 2 + self.add_device(ctrl) + ctrl = VirtualControllerUSB(self.conn, + model="ich9-uhci3") + ctrl.get_master().startport = 4 + self.add_device(ctrl) + + # Set sound device model sound_model = self._lookup_device_param(soundtype, "model") for sound in devlist_func(soundtype): diff --git a/virtinst/osdict.py b/virtinst/osdict.py index e59f7a4..3d6729f 100644 --- a/virtinst/osdict.py +++ b/virtinst/osdict.py @@ -35,6 +35,7 @@ DISK = VirtualDevice.VIRTUAL_DEV_DISK INPUT = VirtualDevice.VIRTUAL_DEV_INPUT SOUND = VirtualDevice.VIRTUAL_DEV_AUDIO VIDEO = VirtualDevice.VIRTUAL_DEV_VIDEO +CONTROLLER = VirtualDevice.VIRTUAL_DEV_CONTROLLER VIRTIO_DISK = { "bus" : [ @@ -72,6 +73,7 @@ DEFAULTS = { "label": None, "pv_cdrom_install": False, "supported": False, + "usb": [(HV_ALL, "default")], "devices" : { # "devname" : { "attribute" : [( ["applicable", "hv-type", list"], @@ -575,6 +577,7 @@ OS_TYPES = { "supported": True, "sortby": "mswin7", "distro": "win", + "usb": [(support.SUPPORT_CONN_HV_CTRL_USB_ICH9, "ich9")] }, }, diff --git a/virtinst/support.py b/virtinst/support.py index 1fb3e28..36ba663 100644 --- a/virtinst/support.py +++ b/virtinst/support.py @@ -65,6 +65,7 @@ SUPPORT_CONN_HV_GRAPHICS_SPICE = 5004 SUPPORT_CONN_HV_CHAR_SPICEVMC = 5005 SUPPORT_CONN_HV_DIRECT_INTERFACE = 5006 SUPPORT_CONN_HV_FILESYSTEM = 5007 +SUPPORT_CONN_HV_CTRL_USB_ICH9 = 5008 # Flags for check_stream_support SUPPORT_STREAM_UPLOAD = 6000 @@ -265,6 +266,11 @@ _support_dict = { ("openvz", 0), ("test", 0)], }, + SUPPORT_CONN_HV_CTRL_USB_ICH9 : { + "version" : 9005, + "force_version" : True, + "drv_version" : [ ("qemu", 15000), ], + }, SUPPORT_STREAM_UPLOAD : { -- 1.7.6

On 08/29/2011 02:13 PM, Marc-André Lureau wrote:
Hi,
Here are a few patches that go with the libvirt series to improve USB support. It adds --controller and --usbredir, as well as providing some new API for virt-manager.
virt-manager and virt-install patches are better sent to virt-tools-list@redhat.com -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (3)
-
Eric Blake
-
Marc-André Lureau
-
Marc-André Lureau