[virt-tools-list] [PATCH 2/4] Add advanced --controller support, augmenting VirtualController
Marc-André Lureau
marcandre.lureau at gmail.com
Fri Sep 2 01:21:17 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-install.1 | 31 ++++++++++-
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 ++++++++++++++
13 files changed, 317 insertions(+), 15 deletions(-)
diff --git a/man/en/virt-install.1 b/man/en/virt-install.1
index 0b73192..7bd35df 100644
--- a/man/en/virt-install.1
+++ b/man/en/virt-install.1
@@ -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 155cfa2..da4142d 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 6b17dd6..9fe2d2f 100644
--- a/tests/clitest.py
+++ b/tests/clitest.py
@@ -598,6 +598,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 "
@@ -652,6 +656,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 ef2c582..e398481 100755
--- a/virt-install
+++ b/virt-install
@@ -490,6 +490,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 869d50e..e817bf7 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
@@ -224,4 +254,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 d9640cb..40cabff 100644
--- a/virtinst/cli.py
+++ b/virtinst/cli.py
@@ -997,6 +997,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 #
#############################
@@ -1065,6 +1075,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",
@@ -1683,6 +1696,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 virt-tools-list
mailing list