
On Tue, 2009-01-13 at 20:49 +0000, Daniel P. Berrange wrote:
Currently when we shutdown the virtual networks are all shutdown too. This is less than useful if we're letting guest VMs hang around post shutdown of libvirtd, because it means we're tearing their network connection out from under them. This patch fixes that allowing networks to survive restarts, and be re-detected next time around.
When starting a virtual network we write the live config into
/var/lib/libvirt/network/$NAME.xml
This is because the bridge device name is potentially auto-generated and we need to keep track of that
We change dnsmasq args to include an explicit pidfile location
/var/run/libvirt/network/$NAME.pid
and also tell it to put itself into the background - ie daemonize. This is because we want dnsmasq to survive the daemon.
Now, when libvirtd starts up it
- Looks for the live config, and if found loads it. - Calls a new method brHasBridge() to see if its desired bridge actually exists (and thus whether the network is running). If it exists,the network is marked active - If DHCP is configured, then reads the dnsmasq PIDfile, and sends kill($PID, 0) to check if the process is actually alive
In addition I cleanup the network code to remove the configFile and autostartLink fields in virNetworkObjPtr, so it matches virDomaiObjPtr usage.
With all this applied you can now restart the daemon, and virbr0 is left happily running.
It all sounds and looks good to me. Some comments below, but nothing major. ...
diff --git a/src/bridge.c b/src/bridge.c --- a/src/bridge.c +++ b/src/bridge.c @@ -163,6 +163,43 @@ int brAddBridge (brControl *ctl ATTRIBUT } #endif
+#ifdef SIOCBRDELBR +int +brHasBridge(brControl *ctl, + const char *name) +{ + struct ifreq ifr; + int len; + + if (!ctl || !name) { + errno = EINVAL; + return -1; + } + + if ((len = strlen(name)) >= BR_IFNAME_MAXLEN) { ^^
Two spaces! Oh noes! Yay for nitpicks!
+ errno = EINVAL; + return -1; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + + strncpy(ifr.ifr_name, name, len); + ifr.ifr_name[len] = '\0'; + + if (ioctl(ctl->fd, SIOCGIFMTU, &ifr)) + return -1;
I'd probably use SIOCGIFFLAGS for this. (Alternative is to use SIOCGIFBR to iterate over bridge names, but the simpler approach is better) ...
diff --git a/src/bridge.h b/src/bridge.h --- a/src/bridge.h +++ b/src/bridge.h @@ -50,6 +50,8 @@ int brAddBridge (brContr char **name); int brDeleteBridge (brControl *ctl, const char *name); +int brHasBridge (brControl *ctl, + const char *name);
int brAddInterface (brControl *ctl, const char *bridge, @@ -58,6 +60,7 @@ int brDeleteInterface (brContr const char *bridge, const char *iface);
+
Spurious. ...
@@ -695,48 +671,64 @@ int virNetworkSaveConfig(virConnectPtr c if (safewrite(fd, xml, towrite) < 0) { virReportSystemError(conn, errno, _("cannot write config file '%s'"), - net->configFile); + configFile); goto cleanup; }
if (close(fd) < 0) { virReportSystemError(conn, errno, _("cannot save config file '%s'"), - net->configFile); + configFile); goto cleanup; }
ret = 0;
cleanup: - VIR_FREE(xml); if (fd != -1) close(fd);
+ VIR_FREE(configFile); + return ret; }
+int virNetworkSaveConfig(virConnectPtr conn, + const char *configDir, + virNetworkDefPtr def) +{ + int ret = -1; + char *xml; + + if (!(xml = virNetworkDefFormat(conn, + def)))
Odd formatting. ...
diff --git a/src/network_driver.c b/src/network_driver.c --- a/src/network_driver.c +++ b/src/network_driver.c @@ -57,6 +57,8 @@ #include "iptables.h" #include "bridge.h"
+#define NETWORK_PID_DIR LOCAL_STATE_DIR "/run/libvirt/network" +#define NETWORK_LIB_DIR LOCAL_STATE_DIR "/lib/libvirt/network"
s/LIB_DIR/STATE_DIR/ perhaps? Also, I'd prefer to see dirs like this created by "make install" and/or RPM install. e.g. with read-only root if you wanted /var/lib/libvirt to be read-only and bind-mount a tmpfs onto /var/lib/libvirt/network
#define VIR_FROM_THIS VIR_FROM_NETWORK
@@ -106,6 +108,64 @@ static struct network_driver *driverStat
static void +networkFindActiveConfigs(struct network_driver *driver) { + unsigned int i; + + for (i = 0 ; i < driver->networks.count ; i++) { + virNetworkObjPtr obj = driver->networks.objs[i]; + virNetworkDefPtr tmp; + char *config; + + virNetworkObjLock(obj); + + if ((config = virNetworkConfigFile(NULL, + NETWORK_LIB_DIR, + obj->def->name)) == NULL) { + virNetworkObjUnlock(obj); + continue; + } + + if (access(config, R_OK) < 0) { + VIR_FREE(config); + virNetworkObjUnlock(obj); + continue; + } + + /* Try and load the live config */ + tmp = virNetworkDefParseFile(NULL,config); ^ Missing whitespace.
...
@@ -377,15 +443,6 @@ networkBuildDnsmasqArgv(virConnectPtr co APPEND_ARG(*argv, i++, "--except-interface"); APPEND_ARG(*argv, i++, "lo");
- /* - * NB, dnsmasq command line arg bug means we need to - * use a single arg '--dhcp-leasefile=path' rather than - * two separate args in '--dhcp-leasefile path' style - */
Worth keeping this comment - e.g. someone could come along and change it do s/=/ /, check that it works with newer dnsmasq and then 6 months later get a report that it doesn't work with older dnsmasq.
- snprintf(buf, sizeof(buf), "--dhcp-leasefile=%s/lib/libvirt/dhcp-%s.leases", - LOCAL_STATE_DIR, network->def->name); - APPEND_ARG(*argv, i++, buf);
--dhcp-leasefile not needed? Worth a comment in the commit log. ...
@@ -442,13 +502,49 @@ dhcpStartDhcpDaemon(virConnectPtr conn, return -1; }
+ if ((err = virFileMakePath(NETWORK_PID_DIR)) < 0) { + virReportSystemError(conn, err, + _("cannot create directory %s"), + NETWORK_PID_DIR); + return -1; + } + if ((err = virFileMakePath(NETWORK_LIB_DIR)) < 0) { + virReportSystemError(conn, err, + _("cannot create directory %s"), + NETWORK_LIB_DIR); + return -1; + } + + if (!(pidfile = virFilePid(NETWORK_PID_DIR, network->def->name))) { + virReportOOMError(conn); + return -1; + } + argv = NULL; - if (networkBuildDnsmasqArgv(conn, network, &argv) < 0) + if (networkBuildDnsmasqArgv(conn, network, pidfile, &argv) < 0) { + VIR_FREE(pidfile); return -1; + }
- ret = virExec(conn, argv, NULL, NULL, - &network->dnsmasqPid, -1, NULL, NULL, VIR_EXEC_NONBLOCK); + if (virRun(conn, argv, NULL) < 0) + goto cleanup;
+ /* + * There really is no race here - when dnsmasq daemonizes, + * its leader process stays around until its child has + * actually written its pidfile. So by time virRun exits + * it has waitpid'd and guarenteed the proess has started
guaranteed
+ * and writtena pid
written a ...
@@ -798,16 +892,24 @@ static int networkStartNetworkDaemon(vir }
-static int networkShutdownNetworkDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, - struct network_driver *driver, - virNetworkObjPtr network) { +static int networkShutdownNetworkDaemon(virConnectPtr conn, + struct network_driver *driver, + virNetworkObjPtr network) { int err; + char *configFile;
networkLog(NETWORK_INFO, _("Shutting down network '%s'\n"), network->def->name);
if (!virNetworkIsActive(network)) return 0;
+ configFile = virNetworkConfigFile(conn, NETWORK_LIB_DIR, network->def->name); + if (!configFile) + return -1; + + unlink(configFile); + VIR_FREE(configFile);
Perhaps rename this to stateFile - I got confused for a second thinking you were deleting the actual config file here.
if (network->dnsmasqPid > 0) kill(network->dnsmasqPid, SIGTERM);
@@ -824,13 +926,10 @@ static int networkShutdownNetworkDaemon( network->def->bridge, strerror(err)); }
+ /* See if its still alive and really really kill it */ if (network->dnsmasqPid > 0 && - waitpid(network->dnsmasqPid, NULL, WNOHANG) != network->dnsmasqPid) { + (kill(network->dnsmasqPid, 0) == 0)) kill(network->dnsmasqPid, SIGKILL); - if (waitpid(network->dnsmasqPid, NULL, 0) != network->dnsmasqPid) - networkLog(NETWORK_WARN, - "%s", _("Got unexpected pid for dnsmasq\n")); - }
Why no more waitpid? Zombies, no? ... Cheers, Mark.