This is the new interface type that sets up a PCI/USB network device
to be assigned to the guest with PCI/USB passthrough after
initializing some network device-specific things from the config
(e.g. MAC address, virtualport profile parameters). Here is an example
of the syntax:
<interface type='hostdev' managed='yes'>
<source>
<address type='pci' domain='0' bus='0' slot='4'
function='0'/>
</source>
<mac address='00:11:22:33:44:55'/>
<address type='pci' domain='0' bus='0' slot='7'
function='0'/>
</interface>
This would assign the PCI card from bus 0 slot 4 function 0 on the
host, to bus 0 slot 7 function 0 on the guest, but would first set the
MAC address of the card to 00:11:22:33:44:55.
Although it's not expected to be used very much, usb network hostdevs
are also supported for completeness. <source> syntax is identical to
that for plain <hostdev> devices, except that the <address> element
should have "type='usb'" added if it's specified:
<interface type='hostdev'>
<source>
<address type='usb' bus='0' device='4'/>
</source>
<mac address='00:11:22:33:44:55'/>
</interface>
If the vendor/product form of usb specification is used, type='usb'
is implied:
<interface type='hostdev'>
<source>
<vendor id='0x0012'/>
<product id='0x24dd'/>
</source>
<mac address='00:11:22:33:44:55'/>
</interface>
---
V2: address Eric's concerns from V1
- check for OOM after strdup
- put in a NOP virDomainHostdevDefClear() rather than just commenting
"there is nothing in the HostdevDef that needs to be freed"
- eliminate inconsistent {} usage.
docs/formatdomain.html.in | 41 +++++
docs/schemas/domaincommon.rng | 50 +++++++
src/conf/domain_conf.c | 154 ++++++++++++++++++--
src/conf/domain_conf.h | 10 ++
src/libvirt_private.syms | 1 +
src/qemu/qemu_command.c | 1 +
src/uml/uml_conf.c | 5 +
src/xenxs/xen_sxpr.c | 1 +
.../qemuxml2argvdata/qemuxml2argv-net-hostdev.xml | 48 ++++++
tests/qemuxml2xmltest.c | 1 +
10 files changed, 297 insertions(+), 15 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 6fcca94..06de0ca 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -2303,6 +2303,47 @@
...
</pre>
+
+ <h5><a name="elementsNICSHostdev">PCI/USB
Passthrough</a></h5>
+
+ <p>
+ A PCI or USB network device (specified by the <source>
+ element) is directly assigned to the guest using generic device
+ passthrough, after first optionally setting the device's MAC
+ address to the configured value, and associating the device with
+ a VEPA or 802.1Qgh capable switch using an optionally specified
+ %lt;virtualport%gt; element (see the examples of virtualport
+ given above for type='direct' network devices).
+ <span class="since">Since 0.9.11</span>
+ </p>
+
+ <p>
+ Note that this "intelligent passthrough" of network devices is
+ very similar to the functionality of a standard <hostdev>
+ device, the difference being that this method allows specifying
+ a MAC address and <virtualport> for the passed-through
+ device. If these capabilities are not required, of if you are
+ using a version of libvirt older than 0.9.11, you should use
+ standard <hostdev> to assign the device to the guest
+ instead of <interface type='hostdev'/>.
+ </p>
+
+<pre>
+ ...
+ <devices>
+ <interface type='hostdev'>
+ <source>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x07' function='0x0'/>
+ </source>
+ <mac address='52:54:00:6d:90:02'>
+ <virtualport type='802.1Qbh'>
+ <parameters profileid='finance'/>
+ </virtualport>
+ </interface>
+ </devices>
+ ...</pre>
+
+
<h5><a name="elementsNICSMulticast">Multicast
tunnel</a></h5>
<p>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 3908733..a905457 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1416,6 +1416,56 @@
</optional>
</interleave>
</group>
+ <group>
+ <attribute name="type">
+ <value>hostdev</value>
+ </attribute>
+ <optional>
+ <attribute name="managed">
+ <choice>
+ <value>yes</value>
+ <value>no</value>
+ </choice>
+ </attribute>
+ </optional>
+ <interleave>
+ <element name="source">
+ <choice>
+ <group>
+ <ref name="usbproduct"/>
+ <optional>
+ <ref name="usbaddress"/>
+ </optional>
+ </group>
+ <element name="address">
+ <choice>
+ <group>
+ <attribute name="type">
+ <value>pci</value>
+ </attribute>
+ <ref name="pciaddress"/>
+ </group>
+ <group>
+ <attribute name="type">
+ <value>usb</value>
+ </attribute>
+ <attribute name="bus">
+ <ref name="usbAddr"/>
+ </attribute>
+ <attribute name="device">
+ <ref name="usbPort"/>
+ </attribute>
+ </group>
+ </choice>
+ </element>
+ </choice>
+ </element>
+ <optional>
+ <ref name="virtualPortProfile"/>
+ </optional>
+ <ref name="interface-options"/>
+ </interleave>
+ </group>
</choice>
</element>
</define>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 94ee634..70e9224 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -286,7 +286,8 @@ VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST,
"network",
"bridge",
"internal",
- "direct")
+ "direct",
+ "hostdev")
VIR_ENUM_IMPL(virDomainNetBackend, VIR_DOMAIN_NET_BACKEND_TYPE_LAST,
"default",
@@ -971,6 +972,10 @@ virDomainActualNetDefFree(virDomainActualNetDefPtr def)
VIR_FREE(def->data.direct.linkdev);
VIR_FREE(def->data.direct.virtPortProfile);
break;
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+ virDomainHostdevDefClear(&def->data.hostdev.def);
+ VIR_FREE(def->data.hostdev.virtPortProfile);
+ break;
default:
break;
}
@@ -1021,6 +1026,11 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
VIR_FREE(def->data.direct.virtPortProfile);
break;
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+ virDomainHostdevDefClear(&def->data.hostdev.def);
+ VIR_FREE(def->data.hostdev.virtPortProfile);
+ break;
+
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_LAST:
break;
@@ -4115,7 +4125,9 @@ cleanup:
static int
virDomainActualNetDefParseXML(xmlNodePtr node,
xmlXPathContextPtr ctxt,
- virDomainActualNetDefPtr *def)
+ virDomainNetDefPtr parent,
+ virDomainActualNetDefPtr *def,
+ unsigned int flags)
{
virDomainActualNetDefPtr actual = NULL;
int ret = -1;
@@ -4123,6 +4135,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node,
xmlNodePtr bandwidth_node = NULL;
char *type = NULL;
char *mode = NULL;
+ char *addrtype = NULL;
if (VIR_ALLOC(actual) < 0) {
virReportOOMError();
@@ -4144,6 +4157,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node,
}
if (actual->type != VIR_DOMAIN_NET_TYPE_BRIDGE &&
actual->type != VIR_DOMAIN_NET_TYPE_DIRECT &&
+ actual->type != VIR_DOMAIN_NET_TYPE_HOSTDEV &&
actual->type != VIR_DOMAIN_NET_TYPE_NETWORK) {
virDomainReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported type '%s' in interface's
<actual> element"),
@@ -4179,6 +4193,34 @@ virDomainActualNetDefParseXML(xmlNodePtr node,
(!(actual->data.direct.virtPortProfile =
virNetDevVPortProfileParse(virtPortNode))))
goto error;
+ } else if (actual->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+ xmlNodePtr virtPortNode = virXPathNode("./virtualport", ctxt);
+ virDomainHostdevDefPtr hostdev = &actual->data.hostdev.def;
+
+ hostdev->parent.type = VIR_DOMAIN_DEVICE_NET;
+
hostdev->parent.data.net = parent;
+ hostdev->info = &parent->info;
+ /* The helper function expects type to already be found and
+ * passed in as a string, since it is in a different place in
+ * NetDef vs HostdevDef.
+ */
+ addrtype = virXPathString("string(./source/address/@type)", ctxt);
+ /* if not explicitly stated, source/vendor implies usb device */
+ if ((!addrtype) && virXPathNode("./source/vendor", ctxt)
&&
+ ((addrtype = strdup("usb")) == NULL)) {
+ virReportOOMError();
+ goto error;
+ }
+ if (virDomainHostdevPartsParse(node, ctxt, NULL, addrtype,
+ hostdev, flags) < 0) {
+ goto error;
+ }
+
+ if (virtPortNode &&
+ (!(actual->data.hostdev.virtPortProfile =
+ virNetDevVPortProfileParse(virtPortNode)))) {
+ goto error;
+ }
}
bandwidth_node = virXPathNode("./bandwidth", ctxt);
@@ -4192,6 +4234,7 @@ virDomainActualNetDefParseXML(xmlNodePtr node,
error:
VIR_FREE(type);
VIR_FREE(mode);
+ VIR_FREE(addrtype);
virDomainActualNetDefFree(actual);
ctxt->node = save_ctxt;
@@ -4213,6 +4256,7 @@ virDomainNetDefParseXML(virCapsPtr caps,
unsigned int flags)
{
virDomainNetDefPtr def;
+ virDomainHostdevDefPtr hostdev;
xmlNodePtr cur;
char *macaddr = NULL;
char *type = NULL;
@@ -4234,6 +4278,7 @@ virDomainNetDefParseXML(virCapsPtr caps,
char *devaddr = NULL;
char *mode = NULL;
char *linkstate = NULL;
+ char *addrtype = NULL;
virNWFilterHashTablePtr filterparams = NULL;
virNetDevVPortProfilePtr virtPort = NULL;
virDomainActualNetDefPtr actual = NULL;
@@ -4286,7 +4331,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
} else if ((virtPort == NULL) &&
((def->type == VIR_DOMAIN_NET_TYPE_DIRECT) ||
(def->type == VIR_DOMAIN_NET_TYPE_NETWORK) ||
- (def->type == VIR_DOMAIN_NET_TYPE_BRIDGE)) &&
+ (def->type == VIR_DOMAIN_NET_TYPE_BRIDGE) ||
+ (def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV)) &&
xmlStrEqual(cur->name, BAD_CAST "virtualport")) {
if (!(virtPort = virNetDevVPortProfileParse(cur)))
goto error;
@@ -4338,8 +4384,10 @@ virDomainNetDefParseXML(virCapsPtr caps,
(flags & VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET) &&
(def->type == VIR_DOMAIN_NET_TYPE_NETWORK) &&
xmlStrEqual(cur->name, BAD_CAST "actual")) {
- if (virDomainActualNetDefParseXML(cur, ctxt, &actual) < 0)
+ if (virDomainActualNetDefParseXML(cur, ctxt, def,
+ &actual, flags) < 0) {
goto error;
+ }
} else if (xmlStrEqual(cur->name, BAD_CAST "bandwidth")) {
if (!(def->bandwidth = virNetDevBandwidthParse(cur)))
goto error;
@@ -4494,6 +4542,30 @@ virDomainNetDefParseXML(virCapsPtr caps,
break;
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+ hostdev = &def->data.hostdev.def;
+ hostdev->parent.type = VIR_DOMAIN_DEVICE_NET;
+
hostdev->parent.data.net = def;
+ hostdev->info = &def->info;
+ /* The helper function expects type to already be found and
+ * passed in as a string, since it is in a different place in
+ * NetDef vs HostdevDef.
+ */
+ addrtype = virXPathString("string(./source/address/@type)", ctxt);
+ /* if not explicitly stated, source/vendor implies usb device */
+ if ((!addrtype) && virXPathNode("./source/vendor", ctxt)
&&
+ ((addrtype = strdup("usb")) == NULL)) {
+ virReportOOMError();
+ goto error;
+ }
+ if (virDomainHostdevPartsParse(node, ctxt, NULL, addrtype,
+ hostdev, flags) < 0) {
+ goto error;
+ }
+ def->data.hostdev.virtPortProfile = virtPort;
+ virtPort = NULL;
+ break;
+
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_LAST:
break;
@@ -4629,6 +4701,7 @@ cleanup:
VIR_FREE(devaddr);
VIR_FREE(mode);
VIR_FREE(linkstate);
+ VIR_FREE(addrtype);
virNWFilterHashTableFree(filterparams);
return def;
@@ -10725,7 +10798,8 @@ virDomainHostdevSourceFormat(virBufferPtr buf,
static int
virDomainActualNetDefFormat(virBufferPtr buf,
- virDomainActualNetDefPtr def)
+ virDomainActualNetDefPtr def,
+ unsigned int flags)
{
int ret = -1;
const char *type;
@@ -10741,14 +10815,12 @@ virDomainActualNetDefFormat(virBufferPtr buf,
return ret;
}
- if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE &&
- def->type != VIR_DOMAIN_NET_TYPE_DIRECT &&
- def->type != VIR_DOMAIN_NET_TYPE_NETWORK) {
- virDomainReportError(VIR_ERR_INTERNAL_ERROR,
- _("unexpected net type %s"), type);
- goto error;
+ virBufferAsprintf(buf, " <actual type='%s'", type);
+ if ((def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) &&
+ def->data.hostdev.def.managed) {
+ virBufferAddLit(buf, " managed='yes'");
}
- virBufferAsprintf(buf, " <actual type='%s'>\n", type);
+ virBufferAddLit(buf, ">\n");
switch (def->type) {
case VIR_DOMAIN_NET_TYPE_BRIDGE:
@@ -10781,8 +10853,26 @@ virDomainActualNetDefFormat(virBufferPtr buf,
goto error;
virBufferAdjustIndent(buf, -8);
break;
- default:
+
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+ virBufferAdjustIndent(buf, 8);
+ if (virDomainHostdevSourceFormat(buf, &def->data.hostdev.def,
+ flags, true) < 0) {
+ return -1;
+ }
+ if (virNetDevVPortProfileFormat(def->data.hostdev.virtPortProfile,
+ buf) < 0) {
+ return -1;
+ }
+ virBufferAdjustIndent(buf, -8);
break;
+
+ case VIR_DOMAIN_NET_TYPE_NETWORK:
+ break;
+ default:
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unexpected net type %s"), type);
+ goto error;
}
virBufferAdjustIndent(buf, 8);
@@ -10810,7 +10900,12 @@ virDomainNetDefFormat(virBufferPtr buf,
return -1;
}
- virBufferAsprintf(buf, " <interface type='%s'>\n", type);
+ virBufferAsprintf(buf, " <interface type='%s'", type);
+ if ((def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) &&
+ def->data.hostdev.def.managed) {
+ virBufferAddLit(buf, " managed='yes'");
+ }
+ virBufferAddLit(buf, ">\n");
virBufferAsprintf(buf,
" <mac
address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n",
@@ -10829,7 +10924,7 @@ virDomainNetDefFormat(virBufferPtr buf,
return -1;
virBufferAdjustIndent(buf, -6);
if ((flags & VIR_DOMAIN_XML_INTERNAL_ACTUAL_NET) &&
- (virDomainActualNetDefFormat(buf, def->data.network.actual) < 0))
+ (virDomainActualNetDefFormat(buf, def->data.network.actual, flags) <
0))
return -1;
break;
@@ -10883,6 +10978,19 @@ virDomainNetDefFormat(virBufferPtr buf,
virBufferAdjustIndent(buf, -6);
break;
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+ virBufferAdjustIndent(buf, 6);
+ if (virDomainHostdevSourceFormat(buf, &def->data.hostdev.def,
+ flags, true) < 0) {
+ return -1;
+ }
+ if (virNetDevVPortProfileFormat(def->data.hostdev.virtPortProfile,
+ buf) < 0) {
+ return -1;
+ }
+ virBufferAdjustIndent(buf, -6);
+ break;
+
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_LAST:
break;
@@ -14136,6 +14244,18 @@ virDomainNetGetActualDirectMode(virDomainNetDefPtr iface)
return iface->data.network.actual->data.direct.mode;
}
+virDomainHostdevDefPtr
+virDomainNetGetActualHostdev(virDomainNetDefPtr iface)
+{
+ if (iface->type == VIR_DOMAIN_NET_TYPE_HOSTDEV)
+ return &iface->data.hostdev.def;
+ if ((iface->type == VIR_DOMAIN_NET_TYPE_NETWORK) &&
+ (iface->data.network.actual->type == VIR_DOMAIN_NET_TYPE_HOSTDEV)) {
+ return &iface->data.network.actual->data.hostdev.def;
+ }
+ return NULL;
+}
+
virNetDevVPortProfilePtr
virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface)
{
@@ -14144,6 +14264,8 @@ virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface)
return iface->data.direct.virtPortProfile;
case VIR_DOMAIN_NET_TYPE_BRIDGE:
return iface->data.bridge.virtPortProfile;
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+ return iface->data.hostdev.virtPortProfile;
case VIR_DOMAIN_NET_TYPE_NETWORK:
if (!iface->data.network.actual)
return NULL;
@@ -14152,6 +14274,8 @@ virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface)
return iface->data.network.actual->data.direct.virtPortProfile;
case VIR_DOMAIN_NET_TYPE_BRIDGE:
return iface->data.network.actual->data.bridge.virtPortProfile;
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+ return iface->data.network.actual->data.hostdev.virtPortProfile;
default:
return NULL;
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 343e48d..6ab5f32 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -694,6 +694,7 @@ enum virDomainNetType {
VIR_DOMAIN_NET_TYPE_BRIDGE,
VIR_DOMAIN_NET_TYPE_INTERNAL,
VIR_DOMAIN_NET_TYPE_DIRECT,
+ VIR_DOMAIN_NET_TYPE_HOSTDEV,
VIR_DOMAIN_NET_TYPE_LAST,
};
@@ -744,6 +745,10 @@ struct _virDomainActualNetDef {
int mode; /* enum virMacvtapMode from util/macvtap.h */
virNetDevVPortProfilePtr virtPortProfile;
} direct;
+ struct {
+ virDomainHostdevDef def;
+ virNetDevVPortProfilePtr virtPortProfile;
+ } hostdev;
} data;
virNetDevBandwidthPtr bandwidth;
};
@@ -797,6 +802,10 @@ struct _virDomainNetDef {
int mode; /* enum virMacvtapMode from util/macvtap.h */
virNetDevVPortProfilePtr virtPortProfile;
} direct;
+ struct {
+ virDomainHostdevDef def;
+ virNetDevVPortProfilePtr virtPortProfile;
+ } hostdev;
} data;
struct {
bool sndbuf_specified;
@@ -1922,6 +1931,7 @@ int virDomainNetGetActualType(virDomainNetDefPtr iface);
const char *virDomainNetGetActualBridgeName(virDomainNetDefPtr iface);
const char *virDomainNetGetActualDirectDev(virDomainNetDefPtr iface);
int virDomainNetGetActualDirectMode(virDomainNetDefPtr iface);
+virDomainHostdevDefPtr virDomainNetGetActualHostdev(virDomainNetDefPtr iface);
virNetDevVPortProfilePtr
virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface);
virNetDevBandwidthPtr
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 921bed0..de02634 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -384,6 +384,7 @@ virDomainNetGetActualBandwidth;
virDomainNetGetActualBridgeName;
virDomainNetGetActualDirectDev;
virDomainNetGetActualDirectMode;
+virDomainNetGetActualHostdev;
virDomainNetGetActualType;
virDomainNetGetActualVirtPortProfile;
virDomainNetIndexByMac;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index db02323..dfac389 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -2722,6 +2722,7 @@ qemuBuildHostNetStr(virDomainNetDefPtr net,
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_DIRECT:
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_LAST:
break;
}
diff --git a/src/uml/uml_conf.c b/src/uml/uml_conf.c
index dbbbfda..0e281ff 100644
--- a/src/uml/uml_conf.c
+++ b/src/uml/uml_conf.c
@@ -257,6 +257,11 @@ umlBuildCommandLineNet(virConnectPtr conn,
_("direct networking type not supported"));
goto error;
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
+ umlReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("hostdev networking type not supported"));
+ goto error;
+
case VIR_DOMAIN_NET_TYPE_LAST:
break;
}
diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c
index 8994cbc..e5df953 100644
--- a/src/xenxs/xen_sxpr.c
+++ b/src/xenxs/xen_sxpr.c
@@ -1956,6 +1956,7 @@ xenFormatSxprNet(virConnectPtr conn,
case VIR_DOMAIN_NET_TYPE_MCAST:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_DIRECT:
+ case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_LAST:
break;
}
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml
b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml
new file mode 100644
index 0000000..504e4f6
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-net-hostdev.xml
@@ -0,0 +1,48 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory>219136</memory>
+ <currentMemory>219136</currentMemory>
+ <vcpu>1</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='hd'/>
+ </os>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu</emulator>
+ <disk type='block' device='disk'>
+ <source dev='/dev/HostVG/QEMUGuest1'/>
+ <target dev='hda' bus='ide'/>
+ <address type='drive' controller='0' bus='0'
target='0' unit='0'/>
+ </disk>
+ <controller type='usb' index='0'/>
+ <controller type='ide' index='0'/>
+ <interface type='hostdev' managed='yes'>
+ <mac address='00:11:22:33:44:55'/>
+ <source>
+ <address type='pci' domain='0x0002' bus='0x03'
slot='0x07' function='0x1'/>
+ </source>
+ <virtualport type='802.1Qbg'>
+ <parameters managerid='11' typeid='1193047'
typeidversion='2' instanceid='09b11c53-8b5c-4eeb-8f00-d84eaa0aaa4f'/>
+ </virtualport>
+ </interface>
+ <interface type='hostdev'>
+ <mac address='11:11:22:33:44:55'/>
+ <source>
+ <address type='usb' bus='0' device='2'/>
+ </source>
+ </interface>
+ <interface type='hostdev'>
+ <mac address='22:11:22:33:44:55'/>
+ <source>
+ <vendor id='0x0012'/>
+ <product id='0x24dd'/>
+ </source>
+ </interface>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 4a2e016..03c75f8 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -171,6 +171,7 @@ mymain(void)
DO_TEST("net-eth");
DO_TEST("net-eth-ifname");
DO_TEST("net-virtio-network-portgroup");
+ DO_TEST("net-hostdev");
DO_TEST("sound");
DO_TEST("net-bandwidth");
--
1.7.7.6