The network driver needs to assign physical devices for use by modes
that use macvtap, keeping track of which physical devices are in use
(and how many instances, when the devices can be shared). Three calls
are added:
networkAllocateActualDevice - finds a physical device for use by the
domain, and sets up the virDomainActualNetDef accordingly.
networkNotifyActualDevice - assumes that the domain was already
running, but libvirtd was restarted, and needs to be notified by each
already-running domain about what interfaces they are using.
networkReleaseActualDevice - decrements the usage count of the
allocated physical device, and frees the virDomainActualNetDef to
avoid later accidentally using the device.
bridge_driver.[hc] - the new APIs
qemu_(command|driver|hotplug|process).c - add calls to the above APIs
in the appropriate places.
tests/Makefile.am - need to include libvirt_driver_network.la whenever
libvirt_driver_qemu.la is linked, to avoid unreferenced symbols
(in functions that are never called by the test programs...)
---
src/network/bridge_driver.c | 398 +++++++++++++++++++++++++++++++++++++++++++
src/network/bridge_driver.h | 6 +
src/qemu/qemu_command.c | 11 ++
src/qemu/qemu_hotplug.c | 41 +++--
src/qemu/qemu_process.c | 26 +++-
tests/Makefile.am | 12 +-
6 files changed, 476 insertions(+), 18 deletions(-)
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 69d4c35..02d7b8b 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -2702,3 +2702,401 @@ int networkRegister(void) {
virRegisterStateDriver(&networkStateDriver);
return 0;
}
+
+/********************************************************/
+
+/* Private API to deal with logical switch capabilities.
+ * These functions are exported so that other parts of libvirt can
+ * call them, but are not part of the public API and not in the
+ * driver's function table. If we ever have more than one network
+ * driver, we will need to present these functions via a second
+ * "backend" function table.
+ */
+
+/* networkAllocateActualDevice:
+ * @iface: the original NetDef from the domain
+ *
+ * Looks up the network reference by iface, allocates a physical
+ * device from that network (if appropriate), and returns with the
+ * virDomainActualNetDef filled in accordingly. If there are no
+ * changes to be made in the netdef, then just leave the actualdef
+ * empty.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+networkAllocateActualDevice(virDomainNetDefPtr iface)
+{
+ struct network_driver *driver = driverState;
+ virNetworkObjPtr network;
+ virNetworkDefPtr netdef;
+ int ret = -1;
+
+ if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
+ return 0;
+
+ virDomainActualNetDefFree(iface->data.network.actual);
+ iface->data.network.actual = NULL;
+
+ networkDriverLock(driver);
+ network = virNetworkFindByName(&driver->networks,
iface->data.network.name);
+ networkDriverUnlock(driver);
+ if (!network) {
+ networkReportError(VIR_ERR_NO_NETWORK,
+ _("no network with matching name '%s'"),
+ iface->data.network.name);
+ goto cleanup;
+ }
+
+ netdef = network->def;
+ if ((netdef->forwardType == VIR_NETWORK_FORWARD_BRIDGE) &&
+ netdef->bridge) {
+
+ /* <forward type='bridge'/> <bridge name='xxx'/>
+ * is VIR_DOMAIN_NET_TYPE_BRIDGE
+ */
+
+ if (VIR_ALLOC(iface->data.network.actual) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_BRIDGE;
+ iface->data.network.actual->data.bridge.brname =
strdup(netdef->bridge);
+ if (!iface->data.network.actual->data.bridge.brname) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ } else if ((netdef->forwardType == VIR_NETWORK_FORWARD_BRIDGE) ||
+ (netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) ||
+ (netdef->forwardType == VIR_NETWORK_FORWARD_VEPA) ||
+ (netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH)) {
+ virVirtualPortProfileParamsPtr virtport = NULL;
+
+ /* <forward type='bridge|private|vepa|passthrough'> are all
+ * VIR_DOMAIN_NET_TYPE_DIRECT.
+ */
+
+ if (VIR_ALLOC(iface->data.network.actual) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ /* Set type=direct and appropriate <source mode='xxx'/> */
+ iface->data.network.actual->type = VIR_DOMAIN_NET_TYPE_DIRECT;
+ switch (netdef->forwardType) {
+ case VIR_NETWORK_FORWARD_BRIDGE:
+ iface->data.network.actual->data.direct.mode =
VIR_MACVTAP_MODE_BRIDGE;
+ break;
+ case VIR_NETWORK_FORWARD_PRIVATE:
+ iface->data.network.actual->data.direct.mode =
VIR_MACVTAP_MODE_PRIVATE;
+ break;
+ case VIR_NETWORK_FORWARD_VEPA:
+ iface->data.network.actual->data.direct.mode = VIR_MACVTAP_MODE_VEPA;
+ break;
+ case VIR_NETWORK_FORWARD_PASSTHROUGH:
+ iface->data.network.actual->data.direct.mode =
VIR_MACVTAP_MODE_PASSTHRU;
+ break;
+ }
+
+ /* Find the most specific virtportprofile and copy it */
+ if (iface->data.network.virtPortProfile) {
+ virtport = iface->data.network.virtPortProfile;
+ } else {
+ virPortGroupDefPtr portgroup
+ = virPortGroupFindByName(netdef, iface->data.network.portgroup);
+ if (portgroup)
+ virtport = portgroup->virtPortProfile;
+ else
+ virtport = netdef->virtPortProfile;
+ }
+ if (virtport) {
+ if (VIR_ALLOC(iface->data.network.actual->data.direct.virtPortProfile)
< 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ /* There are no pointers in a virtualPortProfile, so a shallow copy
+ * is sufficient
+ */
+ *iface->data.network.actual->data.direct.virtPortProfile = *virtport;
+ }
+ /* If there is only a single device, just return it (caller will detect
+ * any error if exclusive use is required but could be acquired).
+ */
+ if (netdef->nForwardIfs == 0) {
+
+ if (!netdef->forwardDev) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' uses a direct mode, but
has no forward dev and no interface pool"),
+ netdef->name);
+ goto cleanup;
+ }
+ iface->data.network.actual->data.direct.linkdev
+ = strdup(netdef->forwardDev);
+ if (!iface->data.network.actual->data.direct.linkdev) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ } else {
+ int ii;
+ virNetworkForwardIfDefPtr dev = NULL;
+
+ /* pick an interface from the pool */
+
+ /* PASSTHROUGH mode, and PRIVATE Mode + 802.1Qbh both require
+ * exclusive access to a device, so current usageCount must be
+ * 0. Other modes can share, so just search for the one with
+ * the lowest usageCount.
+ */
+ if ((netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH) ||
+ ((netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) &&
+ iface->data.network.actual->data.direct.virtPortProfile
&&
+
(iface->data.network.actual->data.direct.virtPortProfile->virtPortType
+ == VIR_VIRTUALPORT_8021QBH))) {
+ /* pick first dev with 0 usageCount */
+
+ for (ii = 0; ii < netdef->nForwardIfs; ii++) {
+ if (netdef->forwardIfs[ii].usageCount == 0) {
+ dev = &netdef->forwardIfs[ii];
+ break;
+ }
+ }
+ } else {
+ /* pick least used dev */
+ dev = &netdef->forwardIfs[0];
+ for (ii = 1; ii < netdef->nForwardIfs; ii++) {
+ if (netdef->forwardIfs[ii].usageCount < dev->usageCount)
+ dev = &netdef->forwardIfs[ii];
+ }
+ }
+ /* dev points at the physical device we want to use */
+ if (!dev) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' requires exclusive access
to interfaces, but none are available"),
+ netdef->name);
+ goto cleanup;
+ }
+ iface->data.network.actual->data.direct.linkdev = strdup(dev->dev);
+ if (!iface->data.network.actual->data.direct.linkdev) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ /* we are now assured of success, so mark the allocation */
+ dev->usageCount++;
+ VIR_DEBUG("Using physical device %s, usageCount %d",
+ dev->dev, dev->usageCount);
+ }
+ }
+
+ ret = 0;
+cleanup:
+ if (network)
+ virNetworkObjUnlock(network);
+ if (ret < 0) {
+ virDomainActualNetDefFree(iface->data.network.actual);
+ iface->data.network.actual = NULL;
+ }
+ return ret;
+}
+
+/* networkNotifyActualDevice:
+ * @iface: the domain's NetDef with an "actual" device already filled in.
+ *
+ * Called to notify the network driver when libvirtd is restarted and
+ * finds an already running domain. If appropriate it will force an
+ * allocation of the actual->direct.linkdev to get everything back in
+ * order.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+networkNotifyActualDevice(virDomainNetDefPtr iface)
+{
+ struct network_driver *driver = driverState;
+ virNetworkObjPtr network;
+ virNetworkDefPtr netdef;
+ char *actualDev;
+ int ret = -1;
+
+ if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
+ return 0;
+
+ if (!iface->data.network.actual ||
+ (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT)) {
+ VIR_DEBUG("Nothing to claim from network %s",
iface->data.network.name);
+ return 0;
+ }
+
+ networkDriverLock(driver);
+ network = virNetworkFindByName(&driver->networks,
iface->data.network.name);
+ networkDriverUnlock(driver);
+ if (!network) {
+ networkReportError(VIR_ERR_NO_NETWORK,
+ _("no network with matching name '%s'"),
+ iface->data.network.name);
+ goto cleanup;
+ }
+
+ actualDev = virDomainNetGetActualDirectDev(iface);
+ if (!actualDev) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("the interface uses a direct mode, but
has no source dev"));
+ goto cleanup;
+ }
+
+ netdef = network->def;
+ if (netdef->nForwardIfs == 0) {
+
+ if (!netdef->forwardDev) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' uses a direct mode, but has
no forward dev and no interface pool"),
+ netdef->name);
+ goto cleanup;
+ }
+
+ if (!STREQ(actualDev, netdef->forwardDev)) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' forward dev='%s'
doesn't match domain's '%s'"),
+ netdef->name, netdef->forwardDev, actualDev);
+ goto cleanup;
+ }
+ } else {
+ int ii;
+ virNetworkForwardIfDefPtr dev = NULL;
+
+ /* find the matching interface in the pool and increment its usageCount */
+
+ for (ii = 0; ii < netdef->nForwardIfs; ii++) {
+ if (STREQ(actualDev, netdef->forwardIfs[ii].dev)) {
+ dev = &netdef->forwardIfs[ii];
+ break;
+ }
+ }
+ /* dev points at the physical device we want to use */
+ if (!dev) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' doesn't have
dev='%s' in use by domain"),
+ netdef->name, actualDev);
+ goto cleanup;
+ }
+
+ /* PASSTHROUGH mode, and PRIVATE Mode + 802.1Qbh both require
+ * exclusive access to a device, so current usageCount must be
+ * 0 in those cases.
+ */
+ if ((dev->usageCount > 0) &&
+ ((netdef->forwardType == VIR_NETWORK_FORWARD_PASSTHROUGH) ||
+ ((netdef->forwardType == VIR_NETWORK_FORWARD_PRIVATE) &&
+ iface->data.network.actual->data.direct.virtPortProfile &&
+
(iface->data.network.actual->data.direct.virtPortProfile->virtPortType
+ == VIR_VIRTUALPORT_8021QBH)))) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' claims dev='%s' is
already in use by a different domain"),
+ netdef->name, actualDev);
+ goto cleanup;
+ }
+ /* we are now assured of success, so mark the allocation */
+ dev->usageCount++;
+ VIR_DEBUG("Using physical device %s, usageCount %d",
+ dev->dev, dev->usageCount);
+ }
+
+ ret = 0;
+cleanup:
+ if (network)
+ virNetworkObjUnlock(network);
+ return ret;
+}
+
+
+/* networkReleaseActualDevice() - Given a domain <interface element
+ * that previously had its <actual> element filled in (and possibly a
+ * physical device allocated to it), free up the physical device for use
+ * by someone else, and free the virDomainActualNetDef.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int
+networkReleaseActualDevice(virDomainNetDefPtr iface)
+{
+ struct network_driver *driver = driverState;
+ virNetworkObjPtr network = NULL;
+ virNetworkDefPtr netdef;
+ char *actualDev;
+ int ret = -1;
+
+ if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
+ return 0;
+
+ if (!iface->data.network.actual ||
+ (virDomainNetGetActualType(iface) != VIR_DOMAIN_NET_TYPE_DIRECT)) {
+ VIR_DEBUG("Nothing to release to network %s",
iface->data.network.name);
+ ret = 0;
+ goto cleanup;
+ }
+
+ networkDriverLock(driver);
+ network = virNetworkFindByName(&driver->networks,
iface->data.network.name);
+ networkDriverUnlock(driver);
+ if (!network) {
+ networkReportError(VIR_ERR_NO_NETWORK,
+ _("no network with matching name '%s'"),
+ iface->data.network.name);
+ goto cleanup;
+ }
+
+ actualDev = virDomainNetGetActualDirectDev(iface);
+ if (!actualDev) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("the interface uses a direct mode, but
has no source dev"));
+ goto cleanup;
+ }
+
+ netdef = network->def;
+ if (netdef->nForwardIfs == 0) {
+
+ if (!netdef->forwardDev) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' uses a direct mode, but has
no forward dev and no interface pool"),
+ netdef->name);
+ goto cleanup;
+ }
+
+ if (!STREQ(actualDev, netdef->forwardDev)) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' forward dev='%s'
doesn't match domain's '%s'"),
+ netdef->name, netdef->forwardDev, actualDev);
+ goto cleanup;
+ }
+ } else {
+ int ii;
+ virNetworkForwardIfDefPtr dev = NULL;
+
+ for (ii = 0; ii < netdef->nForwardIfs; ii++) {
+ if (STREQ(actualDev, netdef->forwardIfs[ii].dev)) {
+ dev = &netdef->forwardIfs[ii];
+ break;
+ }
+ }
+ /* dev points at the physical device we've been using */
+ if (!dev) {
+ networkReportError(VIR_ERR_INTERNAL_ERROR,
+ _("network '%s' doesn't have
dev='%s' in use by domain"),
+ netdef->name, actualDev);
+ goto cleanup;
+ }
+
+ dev->usageCount--;
+ VIR_DEBUG("Releasing physical device %s, usageCount %d",
+ dev->dev, dev->usageCount);
+ }
+
+ ret = 0;
+cleanup:
+ if (network)
+ virNetworkObjUnlock(network);
+ virDomainActualNetDefFree(iface->data.network.actual);
+ iface->data.network.actual = NULL;
+ return ret;
+}
diff --git a/src/network/bridge_driver.h b/src/network/bridge_driver.h
index 2896c84..4f6a54d 100644
--- a/src/network/bridge_driver.h
+++ b/src/network/bridge_driver.h
@@ -29,10 +29,16 @@
# include "internal.h"
# include "network_conf.h"
+# include "domain_conf.h"
# include "command.h"
# include "dnsmasq.h"
int networkRegister(void);
+
+int networkAllocateActualDevice(virDomainNetDefPtr iface);
+int networkNotifyActualDevice(virDomainNetDefPtr iface);
+int networkReleaseActualDevice(virDomainNetDefPtr iface);
+
int networkBuildDhcpDaemonCommandLine(virNetworkObjPtr network,
virCommandPtr *cmdout, char *pidfile,
dnsmasqContext *dctx);
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index d1ecaf4..d6a0c6d 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -37,6 +37,7 @@
#include "domain_nwfilter.h"
#include "qemu_audit.h"
#include "domain_conf.h"
+#include "network/bridge_driver.h"
#include <sys/utsname.h>
#include <sys/stat.h>
@@ -3677,6 +3678,13 @@ qemuBuildCommandLine(virConnectPtr conn,
else
vlan = i;
+ /* If appropriate, grab a physical device from the configured
+ * network's pool of devices, or resolve bridge device name
+ * to the one defined in the network definition.
+ */
+ if (networkAllocateActualDevice(net) < 0)
+ goto error;
+
actualType = virDomainNetGetActualType(net);
if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
@@ -4673,6 +4681,9 @@ qemuBuildCommandLine(virConnectPtr conn,
no_memory:
virReportOOMError();
error:
+ /* free up any resources in the network driver */
+ for (i = 0 ; i < def->nnets ; i++)
+ networkReleaseActualDevice(def->nets[i]);
for (i = 0; i <= last_good_net; i++)
virDomainConfNWFilterTeardown(def->nets[i]);
virBufferFreeAndReset(&rbd_hosts);
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index a5fcdc9..37cfbef 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -39,6 +39,7 @@
#include "files.h"
#include "qemu_cgroup.h"
#include "locking/domain_lock.h"
+#include "network/bridge_driver.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -603,7 +604,8 @@ int qemuDomainAttachNetDevice(virConnectPtr conn,
virDomainDevicePCIAddress guestAddr;
int vlan;
bool releaseaddr = false;
- int actualType = virDomainNetGetActualType(net);
+ bool iface_connected = false;
+ int actualType;
if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_HOST_NET_ADD)) {
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
@@ -611,18 +613,28 @@ int qemuDomainAttachNetDevice(virConnectPtr conn,
return -1;
}
+ /* If appropriate, grab a physical device from the configured
+ * network's pool of devices, or resolve bridge device name
+ * to the one defined in the network definition.
+ */
+ if (networkAllocateActualDevice(net) < 0)
+ goto cleanup;
+
+ actualType = virDomainNetGetActualType(net);
if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE ||
actualType == VIR_DOMAIN_NET_TYPE_NETWORK) {
if ((tapfd = qemuNetworkIfaceConnect(vm->def, conn, driver, net,
priv->qemuCaps)) < 0)
- return -1;
+ goto cleanup;
+ iface_connected = true;
if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, &vhostfd) < 0)
goto cleanup;
} else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) {
if ((tapfd = qemuPhysIfaceConnect(vm->def, conn, driver, net,
priv->qemuCaps,
VIR_VM_OP_CREATE)) < 0)
- return -1;
+ goto cleanup;
+ iface_connected = true;
if (qemuOpenVhostNet(vm->def, net, priv->qemuCaps, &vhostfd) < 0)
goto cleanup;
}
@@ -738,16 +750,19 @@ int qemuDomainAttachNetDevice(virConnectPtr conn,
vm->def->nets[vm->def->nnets++] = net;
cleanup:
- if ((ret != 0) &&
- qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
- (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
- releaseaddr &&
- qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
- net->info.addr.pci.slot) < 0)
- VIR_WARN("Unable to release PCI address on NIC");
+ if (ret < 0) {
+ if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
+ (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
+ releaseaddr &&
+ qemuDomainPCIAddressReleaseSlot(priv->pciaddrs,
+ net->info.addr.pci.slot) < 0)
+ VIR_WARN("Unable to release PCI address on NIC");
- if (ret != 0)
- virDomainConfNWFilterTeardown(net);
+ if (iface_connected)
+ virDomainConfNWFilterTeardown(net);
+
+ networkReleaseActualDevice(net);
+ }
VIR_FREE(nicstr);
VIR_FREE(netstr);
@@ -1629,6 +1644,8 @@ int qemuDomainDetachNetDevice(struct qemud_driver *driver,
}
}
+ networkReleaseActualDevice(detach);
+
if (vm->def->nnets > 1) {
memmove(vm->def->nets + i,
vm->def->nets + i + 1,
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index 9fec746..f4a57ff 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -56,6 +56,7 @@
#include "processinfo.h"
#include "domain_nwfilter.h"
#include "locking/domain_lock.h"
+#include "network/bridge_driver.h"
#include "uuid.h"
#define VIR_FROM_THIS VIR_FROM_QEMU
@@ -2168,6 +2169,20 @@ int qemuProcessStopCPUs(struct qemud_driver *driver,
virDomainObjPtr vm,
static int
+qemuProcessNotifyNets(virDomainDefPtr def)
+{
+ int ii;
+
+ for (ii = 0 ; ii < def->nnets ; ii++) {
+ virDomainNetDefPtr net = def->nets[ii];
+ if (networkNotifyActualDevice(net) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+
+static int
qemuProcessFiltersInstantiate(virConnectPtr conn,
virDomainDefPtr def)
{
@@ -2279,6 +2294,9 @@ qemuProcessReconnect(void *payload, const void *name
ATTRIBUTE_UNUSED, void *opa
if (virSecurityManagerReserveLabel(driver->securityManager, obj) < 0)
goto error;
+ if (qemuProcessNotifyNets(obj->def) < 0)
+ goto error;
+
if (qemuProcessFiltersInstantiate(conn, obj->def))
goto error;
@@ -2898,10 +2916,10 @@ void qemuProcessStop(struct qemud_driver *driver,
qemuDomainReAttachHostDevices(driver, vm->def);
-#if WITH_MACVTAP
def = vm->def;
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
+#if WITH_MACVTAP
if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_DIRECT) {
delMacvtap(net->ifname, net->mac,
virDomainNetGetActualDirectDev(net),
@@ -2910,8 +2928,12 @@ void qemuProcessStop(struct qemud_driver *driver,
driver->stateDir);
VIR_FREE(net->ifname);
}
- }
#endif
+ /* release the physical device (or any other resources used by
+ * this interface in the network driver
+ */
+ networkReleaseActualDevice(net);
+ }
retry:
if ((ret = qemuRemoveCgroup(driver, vm, 0)) < 0) {
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 30b2d95..cf01c23 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -314,23 +314,27 @@ EXTRA_DIST += xml2sexprtest.c sexpr2xmltest.c xmconfigtest.c \
endif
if WITH_QEMU
+
+qemu_LDADDS = ../src/libvirt_driver_qemu.la \
+ ../src/libvirt_driver_network.la
+
qemuxml2argvtest_SOURCES = \
qemuxml2argvtest.c testutilsqemu.c testutilsqemu.h \
testutils.c testutils.h
-qemuxml2argvtest_LDADD = ../src/libvirt_driver_qemu.la $(LDADDS)
+qemuxml2argvtest_LDADD = $(qemu_LDADDS) $(LDADDS)
qemuxml2xmltest_SOURCES = \
qemuxml2xmltest.c testutilsqemu.c testutilsqemu.h \
testutils.c testutils.h
-qemuxml2xmltest_LDADD = ../src/libvirt_driver_qemu.la $(LDADDS)
+qemuxml2xmltest_LDADD = $(qemu_LDADDS) $(LDADDS)
qemuargv2xmltest_SOURCES = \
qemuargv2xmltest.c testutilsqemu.c testutilsqemu.h \
testutils.c testutils.h
-qemuargv2xmltest_LDADD = ../src/libvirt_driver_qemu.la $(LDADDS)
+qemuargv2xmltest_LDADD = $(qemu_LDADDS) $(LDADDS)
qemuhelptest_SOURCES = qemuhelptest.c testutils.c testutils.h
-qemuhelptest_LDADD = ../src/libvirt_driver_qemu.la $(LDADDS)
+qemuhelptest_LDADD = $(qemu_LDADDS) $(LDADDS)
else
EXTRA_DIST += qemuxml2argvtest.c qemuxml2xmltest.c qemuargv2xmltest.c qemuhelptest.c
testutilsqemu.c testutilsqemu.h
endif
--
1.7.3.4