Before the refactoring that properly separated the network driver from
the hypervisor driver and forced all interaction to go through public
APIs, all network usage counters were zeroed when the network driver
was initialized, and the network driver's now-deprecated
"semi-private" API networkNotifyActualDevice() was called for every
interface of every domain as each hypervisor "reconnected" its domains
during a libvirtd restart, and this would refresh the usage count for
each network.
Post-driver-split, during libvirtd restart/reconnection of the running
domains, the function virDomainNetNotifyActualDevice() is called by
each hypervisor driver for every interface of every domain restart,
and this function has code to re-register interfaces, but it only
calls into the network driver to re-register those ports that don't
already have a valid portid (ie. one that is not simply all 0),
assuming that those with valid portids are already known (and counted)
by the network driver.
commit 7ab9bdd47 recently modified the network driver so that, in most
cases, it properly resyncs each network's connection count during
libvirtd (or maybe virtnetworkd) restart by iterating through the
network's port list. This doesn't account for the case where a network
is destroyed and restarted while there are running domains that have
active ports on the network. In that case, the entire port list and
connection count for that network is lost, and now even a restart of
libvirtd/virtnetworkd/virtqemud, which in the past would resync the
connection count, doesn't help (the network driver thinks there are no
active ports, while the hypervisor driver knows about all the active
ports, but mistakenly believes that the network driver also knows).
The solution to this is to not just bypass valid portids during the
call to virDomainNetworkNotifyActualDevice(). Instead, we query the
network driver about the portid that was preserved in the domain
status, and if it is not registered, we register it.
(NB: while it would technically be correct to just generate a new
portid for these cases, it makes for less churn in portids (and thus
may make troubleshooting simpler) if we make the small fix to
virDomainNetDefActualToNetworkPort() that preserves existing valid
portids rather than unconditionally generating a new one.)
Signed-off-by: Laine Stump <laine(a)redhat.com>
---
src/conf/domain_conf.c | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index b6fa802523..d1e7ac84e8 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -30749,8 +30749,9 @@ virDomainNetDefActualToNetworkPort(virDomainDefPtr dom,
if (VIR_ALLOC(port) < 0)
return NULL;
- /* Bad - we need to preserve original port uuid */
- if (virUUIDGenerate(port->uuid) < 0) {
+ if (virUUIDIsValid(iface->data.network.portid)) {
+ memcpy(port->uuid, iface->data.network.portid, VIR_UUID_BUFLEN);
+ } else if (virUUIDGenerate(port->uuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("Failed to generate UUID"));
goto error;
@@ -30883,6 +30884,23 @@ virDomainNetCreatePort(virConnectPtr conn,
return -1;
if (flags & VIR_NETWORK_PORT_CREATE_RECLAIM) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ char macstr[VIR_MAC_STRING_BUFLEN];
+
+ virUUIDFormat(iface->data.network.portid, uuidstr);
+ virMacAddrFormat(&iface->mac, macstr);
+
+ /* if the port is already registered, then we are done */
+ if (virUUIDIsValid(iface->data.network.portid) &&
+ (port = virNetworkPortLookupByUUID(net, iface->data.network.portid))) {
+ VIR_DEBUG("network: %s domain: %s mac: %s port: %s - already registered,
skipping",
+ iface->data.network.name, dom->name, macstr, uuidstr);
+ return 0;
+ }
+
+ /* otherwise we need to create a new port */
+ VIR_DEBUG("network: %s domain: %s mac: %s port: %s - not found,
reclaiming",
+ iface->data.network.name, dom->name, macstr, uuidstr);
if (!(portdef = virDomainNetDefActualToNetworkPort(dom, iface)))
return -1;
} else {
@@ -30931,10 +30949,9 @@ virDomainNetNotifyActualDevice(virConnectPtr conn,
{
virDomainNetType actualType = virDomainNetGetActualType(iface);
- if (!virUUIDIsValid(iface->data.network.portid)) {
- if (virDomainNetCreatePort(conn, dom, iface,
- VIR_NETWORK_PORT_CREATE_RECLAIM) < 0)
- return;
+ if (virDomainNetCreatePort(conn, dom, iface,
+ VIR_NETWORK_PORT_CREATE_RECLAIM) < 0) {
+ return;
}
if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
--
2.21.0