[libvirt] [PATCH 2/4] Add advanced --controller support, augmenting VirtualController

Marc-André Lureau marcandre.lureau at gmail.com
Thu Aug 25 22:48:52 UTC 2011


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                         |   34 ++++++++++-
 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, 345 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..e30bc64 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-23" "" "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,36 @@ 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"
+Define the master bus, currently applicable to \s-1USB\s0 companion controllers.
+The value expected is of the form 'bus:startport'.
+.RE
+.RS 2
+.Sp
+Example:
+.IP "\fB\-\-controller usb,model=ich9\-uhci2,address=0:0:4.7,master=0:2\fR" 4
+.IX Item "--controller usb,model=ich9-uhci2,address=0:0:4.7,master=0: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..246b5cd 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: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




More information about the libvir-list mailing list