xenserver: Gracefully refresh network UUIDs on pool join or leave.
authorBen Pfaff <blp@nicira.com>
Mon, 26 Apr 2010 21:18:32 +0000 (14:18 -0700)
committerJustin Pettit <jpettit@nicira.com>
Mon, 26 Apr 2010 23:41:35 +0000 (16:41 -0700)
The vswitch database is supposed to maintain an up-to-date UUID for the
system's networks in the Bridge table as external-ids:network-uuids.  On
XenServer systems, /opt/xensource/libexec/interface-reconfigure updates
these fields as bridges are brought up and down.  Most of the time, that is
sufficient.  However, this is one exception: when a XenServer host enters
or leaves a pool, interface-reconfigure is not invoked, and neither is any
other script.  So this commit introduces a new, XenServer-specific daemon
that monitors the XenServer's pool membership status and refreshes the
network UUIDs (by invoking the refresh-network-uuids script) if it changes.

Bug #2097.

lib/vlog-modules.def
xenserver/automake.mk
xenserver/etc_init.d_openvswitch
xenserver/openvswitch-xen.spec
xenserver/ovs-xenserverd.8.in [new file with mode: 0644]
xenserver/ovs-xenserverd.c [new file with mode: 0644]
xenserver/usr_share_openvswitch_scripts_sysconfig.template

index 6c2be7b..5c836d6 100644 (file)
@@ -97,5 +97,6 @@ VLOG_MODULE(wcelim)
 VLOG_MODULE(vswitchd)
 VLOG_MODULE(vt)
 VLOG_MODULE(xenserver)
+VLOG_MODULE(xenserverd)
 
 #undef VLOG_MODULE
index b451bbb..1340b0d 100644 (file)
@@ -25,3 +25,11 @@ EXTRA_DIST += \
        xenserver/usr_sbin_xen-bugtool \
        xenserver/usr_share_openvswitch_scripts_refresh-network-uuids \
        xenserver/usr_share_openvswitch_scripts_sysconfig.template
+
+noinst_PROGRAMS += xenserver/ovs-xenserverd
+xenserver_ovs_xenserverd_SOURCES = xenserver/ovs-xenserverd.c
+xenserver_ovs_xenserverd_LDADD = lib/libopenvswitch.a
+
+man_MANS += xenserver/ovs-xenserverd.8
+DISTCLEANFILES += xenserver/ovs-xenserverd.8
+EXTRA_DIST += xenserver/ovs-xenserverd.8.in
index 51b1d50..5adf398 100755 (executable)
@@ -81,11 +81,16 @@ fi
 : ${BRCOMPATD_VALGRIND_LOG:=}
 : ${BRCOMPATD_VALGRIND_OPT:=}
 
+# Config variables specific to ovs-xenserverd
+: ${XENSERVERD_PIDFILE:=/var/run/openvswitch/ovs-xenserverd.pid}
+: ${XENSERVERD_RUN_DIR:=/var/xen/openvswitch}
+
 # Full paths to executables & modules
 ovsdb_server="/usr/sbin/ovsdb-server"
 ovsdb_tool="/usr/bin/ovsdb-tool"
 vswitchd="/usr/sbin/ovs-vswitchd"
 brcompatd="/usr/sbin/ovs-brcompatd"
+xenserverd="/usr/sbin/ovs-xenserverd"
 dpctl="/usr/bin/ovs-dpctl"
 appctl="/usr/bin/ovs-appctl"
 ofctl="/usr/bin/ovs-ofctl"
@@ -293,6 +298,17 @@ function start_brcompatd {
     fi
 }
 
+function start_xenserverd {
+    if [ ! -d "$XENSERVERD_RUN_DIR" ]; then
+        install -d -m 755 -o root -g root "$XENSERVERD_RUN_DIR"
+    fi
+    cd "$XENSERVERD_RUN_DIR"
+
+    install -d -m 755 -o root -g root `dirname $XENSERVERD_PIDFILE`
+    action "Starting ovs-xenserverd" "$xenserverd" --no-chdir --pidfile=$XENSERVERD_PIDFILE --detach $monitor_opt -vANY:CONSOLE:EMER
+    fi
+}
+
 function stop_ovsdb_server {
     if [ -f "$OVSDB_SERVER_PIDFILE" ]; then
         local pid=$(cat "$OVSDB_SERVER_PIDFILE")
@@ -317,6 +333,14 @@ function stop_brcompatd {
     fi
 }
 
+function stop_xenserverd {
+    if [ -f "$XENSERVERD_PIDFILE" ]; then
+        local pid=$(cat "$XENSERVERD_PIDFILE")
+        action "Killing ovs-xenserverd ($pid)" kill -TERM $pid
+        rm -f "$XENSERVERD_PIDFILE"
+    fi
+}
+
 function restart_approval {
     if test ! -t 0; then
         # Don't prompt if invoked non-interactively.
@@ -391,10 +415,12 @@ function start {
     if [ "${ENABLE_BRCOMPAT}" = "y" ] ; then
         start_brcompatd
     fi
+    start_xenserverd
     touch /var/lock/subsys/openvswitch
 }
 
 function stop {
+    stop_xenserverd
     stop_brcompatd
     stop_vswitchd
     stop_ovsdb_server
@@ -430,12 +456,14 @@ case "$1" in
         status -p "$OVSDB_SERVER_PIDFILE" ovsdb-server &&
         status -p "$VSWITCHD_PIDFILE" ovs-vswitchd &&
         (test "$ENABLE_BRCOMPAT" != "y" || 
-            status -p "$BRCOMPATD_PIDFILE" ovs-brcompatd)
+            status -p "$BRCOMPATD_PIDFILE" ovs-brcompatd) &&
+        status -p "$XENSERVERD_PIDFILE" ovs-xenserverd
         ;;
     version)
         /usr/sbin/ovsdb-server -V
         /usr/sbin/ovs-vswitchd -V
         /usr/sbin/ovs-brcompatd -V
+        /usr/sbin/ovs-xenserverd -V
         ;;
     help)
         printf "openvswitch [start|stop|restart|unload|status|version]\n"
index c796a1b..bd9d27a 100644 (file)
@@ -79,6 +79,7 @@ install -m 755 xenserver/usr_sbin_xen-bugtool \
              $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/xen-bugtool
 install -m 755 xenserver/usr_sbin_brctl \
              $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/brctl
+install -m 755 xenserver/ovs-xenserverd $RPM_BUILD_ROOT/usr/sbin/
 install -m 755 xenserver/usr_share_openvswitch_scripts_sysconfig.template \
          $RPM_BUILD_ROOT/usr/share/openvswitch/scripts/sysconfig.template
 install -d -m 755 $RPM_BUILD_ROOT/usr/lib/xsconsole/plugins-base
@@ -351,6 +352,7 @@ fi
 /usr/share/openvswitch/vswitch.ovsschema
 /usr/sbin/ovs-brcompatd
 /usr/sbin/ovs-vswitchd
+/usr/sbin/ovs-xenserverd
 /usr/sbin/ovsdb-server
 /usr/bin/ovs-appctl
 /usr/bin/ovs-dpctl
@@ -369,6 +371,7 @@ fi
 /usr/share/man/man8/ovs-ofctl.8.gz
 /usr/share/man/man8/ovs-vsctl.8.gz
 /usr/share/man/man8/ovs-vswitchd.8.gz
+/usr/share/man/man8/ovs-xenserverd.8.gz
 /var/lib/openvswitch
 %exclude /usr/lib/xsconsole/plugins-base/*.pyc
 %exclude /usr/lib/xsconsole/plugins-base/*.pyo
diff --git a/xenserver/ovs-xenserverd.8.in b/xenserver/ovs-xenserverd.8.in
new file mode 100644 (file)
index 0000000..87e76d8
--- /dev/null
@@ -0,0 +1,40 @@
+.\" -*- nroff -*-
+.de IQ
+.  br
+.  ns
+.  IP "\\$1"
+..
+.TH ovs\-xenserverd 8 "April 2010" "Open vSwitch" "Open vSwitch Manual"
+.ds PN ovs\-xenserverd
+.
+.SH NAME
+ovs\-xenserverd \- Open vSwitch daemon for XenServer-specific functionality
+.
+.SH SYNOPSIS
+\fBovs\-xenserverd \fR[\fIoptions\fR]
+.
+.SH DESCRIPTION
+A daemon that provides XenServer-specific functionality for Open
+vSwitch.  Currently, its only purpose is to ensure that network UUIDs
+in the Open vSwitch database are kept up-to-date when the XenServer
+host joins or leaves a pool.  It does so by running
+\fB@pkgdatadir@/scripts/refresh-network-uuids\fR.
+.PP
+\fBovs\-xenserverd\fR is useful only on Citrix XenServer hosts.
+Running it on any other kind of host is harmless but not useful.
+.SH OPTIONS
+.so lib/daemon.man
+.SS "Public Key Infrastructure Options"
+.so lib/vlog.man
+.so lib/common.man
+.
+.SH "RUNTIME MANAGEMENT COMMANDS"
+\fBovs\-appctl\fR(8) can send commands to a running
+\fBovs\-xenserverd\fR process.  The currently supported commands are
+described below.
+.so lib/vlog-unixctl.man
+.SH "SEE ALSO"
+.BR ovs\-appctl (8),
+.BR ovs\-vswitchd (8),
+.BR ovsdb\-server (1),
+\fBINSTALL.Linux\fR in the Open vSwitch distribution.
diff --git a/xenserver/ovs-xenserverd.c b/xenserver/ovs-xenserverd.c
new file mode 100644 (file)
index 0000000..72617ae
--- /dev/null
@@ -0,0 +1,228 @@
+/* Copyright (c) 2010 Nicira Networks
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "command-line.h"
+#include "daemon.h"
+#include "dirs.h"
+#include "poll-loop.h"
+#include "process.h"
+#include "socket-util.h"
+#include "timeval.h"
+#include "unixctl.h"
+#include "util.h"
+
+#define THIS_MODULE VLM_xenserverd
+#include "vlog.h"
+
+static void parse_options(int argc, char *argv[]);
+static void usage(void) NO_RETURN;
+
+static void network_uuid_refresh_run(void);
+static void network_uuid_refresh_wait(void);
+
+int
+main(int argc, char *argv[])
+{
+    struct unixctl_server *unixctl;
+    int retval;
+
+    proctitle_init(argc, argv);
+    set_program_name(argv[0]);
+    time_init();
+    vlog_init();
+    parse_options(argc, argv);
+    signal(SIGPIPE, SIG_IGN);
+    process_init();
+
+    die_if_already_running();
+    daemonize_start();
+
+    retval = unixctl_server_create(NULL, &unixctl);
+    if (retval) {
+        exit(EXIT_FAILURE);
+    }
+
+    daemonize_complete();
+
+    for (;;) {
+        network_uuid_refresh_run();
+        unixctl_server_run(unixctl);
+
+        network_uuid_refresh_wait();
+        unixctl_server_wait(unixctl);
+
+        poll_block();
+    }
+
+    return 0;
+}
+
+
+static void
+parse_options(int argc, char *argv[])
+{
+    enum {
+        VLOG_OPTION_ENUMS
+    };
+    static struct option long_options[] = {
+        {"help",        no_argument, 0, 'h'},
+        {"version",     no_argument, 0, 'V'},
+        DAEMON_LONG_OPTIONS,
+        VLOG_LONG_OPTIONS,
+        {0, 0, 0, 0},
+    };
+    char *short_options = long_options_to_short_options(long_options);
+
+    for (;;) {
+        int c;
+
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 'H':
+        case 'h':
+            usage();
+
+        case 'V':
+            OVS_PRINT_VERSION(0, 0);
+            exit(EXIT_SUCCESS);
+
+        VLOG_OPTION_HANDLERS
+        DAEMON_OPTION_HANDLERS
+
+        case '?':
+            exit(EXIT_FAILURE);
+
+        default:
+            abort();
+        }
+    }
+    free(short_options);
+
+    if (optind != argc) {
+        ovs_fatal(0, "no non-option arguments accepted");
+    }
+}
+
+static void
+usage(void)
+{
+    printf("%s: Open vSwitch daemon for XenServer-specific functionality\n"
+           "usage: %s [OPTIONS]\n", program_name, program_name);
+    daemon_usage();
+    vlog_usage();
+    printf("\nOther options:\n"
+           "  -h, --help              display this help message\n"
+           "  -V, --version           display version information\n");
+    exit(EXIT_SUCCESS);
+}
+\f
+/* Network UUID refreshing.
+ *
+ * The vswitch database is supposed to maintain an up-to-date UUID for the
+ * system's networks in the Bridge table as external-ids:network-uuids.  On
+ * XenServer systems, /opt/xensource/libexec/interface-reconfigure updates
+ * these fields as bridges are brought up and down.  Most of the time, that is
+ * sufficient.  However, this is one exception: when a XenServer host enters or
+ * leaves a pool, interface-reconfigure is not invoked, and neither is any
+ * other script.  So we need to monitor the XenServer's pool membership status
+ * and refresh the network UUIDs (by invoking the refresh-network-uuids script)
+ * if it changes.
+ *
+ * This functionality should be harmless on non-XenServer systems, since they
+ * will have neither /etc/xensource/pool.conf nor refresh-network-uuids.
+ */
+
+/* Timestamp of /etc/xensource/pool.conf, or zeros if it does not exist. */
+static struct timespec pool_conf_mtime;
+
+/* The executing instance of refresh-network-uuids, or NULL if none. */
+static struct process *refresh_script;
+
+static void
+network_uuid_refresh_run(void)
+{
+    struct timespec new_mtime;
+
+    /* If a script is running, don't do anything until it finishes. */
+    if (refresh_script) {
+        char *s;
+
+        if (!process_exited(refresh_script)) {
+            return;
+        }
+
+        s = process_status_msg(process_status(refresh_script));
+        VLOG_INFO("refresh-network-uuids exited, %s", s);
+        free(s);
+
+        process_destroy(refresh_script);
+        refresh_script = NULL;
+    }
+
+    /* Otherwise, check for a change in timestamp.
+     *
+     * (We will always detect a change in timestamp when we start up.  That's
+     * good, since it means that the refresh-network-uuids script gets
+     * thoroughly tested and we can't miss pool changes that happen when
+     * ovs-vswitchd isn't running.)  */
+    get_mtime("/etc/xensource/pool.conf", &new_mtime);
+    if (new_mtime.tv_sec != pool_conf_mtime.tv_sec
+        || new_mtime.tv_nsec != pool_conf_mtime.tv_nsec) {
+        struct stat s;
+        char *argv[2];
+
+        argv[0] = xasprintf("%s/scripts/refresh-network-uuids",
+                            ovs_pkgdatadir);
+        argv[1] = NULL;
+
+        if (!stat(argv[0], &s)) {
+            int error = process_start(argv, NULL, 0, NULL, 0, &refresh_script);
+            if (error) {
+                VLOG_ERR("failed to refresh network UUIDs: %s could "
+                         "not be started (%s)", argv[0], strerror(error));
+            } else {
+                VLOG_INFO("refreshing network UUIDs: started %s", argv[0]);
+            }
+        } else {
+            VLOG_ERR("failed to refresh network UUIDs: could not stat %s (%s)",
+                     argv[0], strerror(errno));
+        }
+
+        pool_conf_mtime = new_mtime;
+        free(argv[0]);
+    }
+}
+
+void
+network_uuid_refresh_wait(void)
+{
+    if (refresh_script) {
+        process_wait(refresh_script);
+    } else if (pool_conf_mtime.tv_sec) {
+        poll_timer_wait(1000);
+    }
+}
index c639df8..6492931 100644 (file)
 #     This option's value is honored only when BRCOMPATD_VALGRIND_LOG is
 #     set to a nonempty string.
 # BRCOMPATD_VALGRIND_OPT=""
+
+# XENSERVERD_PIDFILE: File in which to store the pid of the running
+#     ovs-xenserved.
+# XENSERVERD_PIDFILE=/var/run/openvswitch/ovs-xenserverd.pid
+
+# XENSERVERD_RUN_DIR: Set the directory in which ovs-xenserverd should be
+#     run.  This mainly affects where core files will be placed.
+# XENSERVERD_RUN_DIR=/var/xen/openvswitch