[libvirt] PATCH: Allow virtual networks to survive daemon restarts
Mark McLoughlin
markmc at redhat.com
Wed Jan 14 08:55:46 UTC 2009
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.
More information about the libvir-list
mailing list