Merge branch 'master-readonly-records' into foo
authorRonnie Sahlberg <ronniesahlberg@gmail.com>
Sun, 11 Sep 2011 23:34:34 +0000 (09:34 +1000)
committerRonnie Sahlberg <ronniesahlberg@gmail.com>
Sun, 11 Sep 2011 23:34:34 +0000 (09:34 +1000)
Conflicts:

Makefile.in
tools/ctdb.c

67 files changed:
Makefile.in
client/ctdb_client.c
common/cmdline.c
common/system_aix.c
common/system_common.c
common/system_linux.c
config/ctdb.sysconfig
config/events.d/00.ctdb
config/events.d/10.interface
config/events.d/13.per_ip_routing
config/events.d/60.nfs
config/functions
configure.ac
include/ctdb.h
include/ctdb_client.h
include/ctdb_private.h
lib/tevent/tevent_util.c
libctdb/ctdb.c
packaging/RPM/ctdb.spec.in
server/ctdb_logging.c
server/ctdb_ltdb_server.c
server/ctdb_recoverd.c
server/ctdb_takeover.c
server/ctdb_tunables.c
server/eventscript.c
tests/eventscripts/common.sh
tests/eventscripts/etc/sysconfig/ctdb [new symlink]
tests/eventscripts/multievent/10.interface.001.sh [new file with mode: 0755]
tests/eventscripts/multievent/60.nfs.001.sh [new file with mode: 0755]
tests/eventscripts/multievent/60.nfs.002.sh [new file with mode: 0755]
tests/eventscripts/multievent/60.nfs.003.sh [new file with mode: 0755]
tests/eventscripts/multievent/60.nfs.004.sh [new file with mode: 0755]
tests/eventscripts/multievent/60.nfs.005.sh [new file with mode: 0755]
tests/eventscripts/multievent/60.nfs.006.sh [new file with mode: 0755]
tests/eventscripts/run_tests.sh
tests/eventscripts/simple/40.vsftpd.monitor.001.sh
tests/eventscripts/simple/41.httpd.monitor.001.sh
tests/eventscripts/simple/50.samba.monitor.107.sh [new file with mode: 0755]
tests/eventscripts/simple/50.samba.monitor.108.sh [new file with mode: 0755]
tests/eventscripts/simple/50.samba.monitor.109.sh [new file with mode: 0755]
tests/eventscripts/simple/50.samba.monitor.110.sh [new file with mode: 0755]
tests/eventscripts/simple/50.samba.monitor.111.sh [new file with mode: 0755]
tests/eventscripts/simple/60.nfs.monitor.161.sh [new file with mode: 0755]
tests/eventscripts/simple/60.nfs.monitor.162.sh [new file with mode: 0755]
tests/eventscripts/stubs/ctdb
tests/eventscripts/stubs/exportfs
tests/eventscripts/stubs/netstat
tests/eventscripts/stubs/nmap [new file with mode: 0755]
tests/eventscripts/stubs/sleep [new file with mode: 0755]
tests/src/ctdb_bench.c
tests/src/ctdb_fetch.c
tests/src/ctdb_fetch_one.c
tests/src/ctdb_persistent.c
tests/src/ctdb_randrec.c
tests/src/ctdb_store.c
tests/src/ctdb_transaction.c
tests/src/ctdb_traverse.c
tools/ctdb.c
tools/ctdb_vacuum.c
utils/pmda/Install [new file with mode: 0644]
utils/pmda/README [new file with mode: 0644]
utils/pmda/Remove [new file with mode: 0644]
utils/pmda/domain.h [new file with mode: 0644]
utils/pmda/help [new file with mode: 0644]
utils/pmda/pmda_ctdb.c [new file with mode: 0644]
utils/pmda/pmns [new file with mode: 0644]
utils/pmda/root [new file with mode: 0644]

index 80b0a02f5583b27c73d140dbf44e870f0a72ed34..7ec430c1649659c9a326c3aee17e93728cfdc6d3 100755 (executable)
@@ -29,6 +29,10 @@ POPT_LIBS = @POPT_LIBS@
 POPT_CFLAGS = @POPT_CFLAGS@
 POPT_OBJ = @POPT_OBJ@
 
+PMDA_LIBS = -lpcp -lpcp_pmda
+PMDA_INSTALL = @CTDB_PMDA_INSTALL@
+PMDA_DEST_DIR = /var/lib/pcp/pmdas
+
 CFLAGS=-g -I$(srcdir)/include -Iinclude -Ilib -Ilib/util -I$(srcdir) \
        -I@tallocdir@ -I@tdbdir@/include -I@libreplacedir@ \
        -DVARDIR=\"$(localstatedir)\" -DETCDIR=\"$(etcdir)\" \
@@ -75,7 +79,8 @@ TEST_BINS=tests/bin/ctdb_bench tests/bin/ctdb_fetch tests/bin/ctdb_fetch_one \
        tests/bin/ctdb_takeover_tests tests/bin/ctdb_update_record \
        @INFINIBAND_BINS@
 
-BINS = bin/ctdb @CTDB_SCSI_IO@ bin/smnotify bin/ping_pong bin/ltdbtool
+BINS = bin/ctdb @CTDB_SCSI_IO@ bin/smnotify bin/ping_pong bin/ltdbtool @CTDB_PMDA@
+
 SBINS = bin/ctdbd
 
 DIRS = lib bin tests/bin
@@ -152,6 +157,9 @@ bin/ping_pong: utils/ping_pong/ping_pong.o
        @echo Linking $@
        @$(CC) $(CFLAGS) -o $@ utils/ping_pong/ping_pong.o
 
+bin/pmdactdb: $(CTDB_CLIENT_OBJ) utils/pmda/pmda_ctdb.o
+       @echo Linking $@
+       @$(CC) $(CFLAGS) -o $@ utils/pmda/pmda_ctdb.o $(CTDB_CLIENT_OBJ) $(LIB_FLAGS) $(PMDA_LIBS)
 
 tests/bin/rb_test: $(CTDB_CLIENT_OBJ) tests/src/rb_test.o 
        @echo Linking $@
@@ -171,7 +179,7 @@ tests/bin/ctdb_fetch_one: $(CTDB_CLIENT_OBJ) tests/src/ctdb_fetch_one.o
 
 tests/bin/ctdb_fetch_lock_once: libctdb/libctdb.a tests/src/ctdb_fetch_lock_once.o 
        @echo Linking $@
-       @$(CC) $(CFLAGS) -o $@ tests/src/ctdb_fetch_lock_once.o libctdb/libctdb.a -ltdb $(LIB_FLAGS)
+       @$(CC) $(CFLAGS) -o $@ tests/src/ctdb_fetch_lock_once.o @TDB_OBJ@ @POPT_OBJ@ libctdb/libctdb.a $(LIB_FLAGS)
 
 tests/bin/ctdb_fetch_readonly_once: $(CTDB_CLIENT_OBJ) tests/src/ctdb_fetch_readonly_once.o
        @echo Linking $@
@@ -242,7 +250,7 @@ distclean: clean
        rm -f config.log config.status config.cache config.h
        rm -f Makefile
 
-install: all
+install: all $(PMDA_INSTALL)
        mkdir -p $(DESTDIR)$(libdir)/pkgconfig
        mkdir -p $(DESTDIR)$(bindir)
        mkdir -p $(DESTDIR)$(sbindir)
@@ -295,6 +303,12 @@ install: all
        if [ ! -f $(DESTDIR)$(etcdir)/ctdb/notify.sh ];then ${INSTALLCMD} -m 755 config/notify.sh $(DESTDIR)$(etcdir)/ctdb; fi
        if [ ! -f $(DESTDIR)$(etcdir)/ctdb/ctdb-crash-cleanup.sh ];then ${INSTALLCMD} -m 755 config/ctdb-crash-cleanup.sh $(DESTDIR)$(etcdir)/ctdb; fi
 
+install_pmda:
+       $(INSTALLCMD) -m 755 -d $(PMDA_DEST_DIR)
+       $(INSTALLCMD) -m 755 pmda/Install pmda/Remove $(PMDA_DEST_DIR)
+       $(INSTALLCMD) -m 644 pmda/pmns pmda/domain.h pmda/help pmda/README $(PMDA_DEST_DIR)
+       $(INSTALLCMD) -m 755 bin/pmdactdb $(PMDA_DEST_DIR)
+
 test: all
        tests/run_tests.sh
 
index b3061a855c5e5a958037e70d88fc5412c549c90f..89eeb4836a14bca81a422765b558854f1fb777e0 100644 (file)
@@ -187,7 +187,7 @@ static void ctdb_client_reply_control(struct ctdb_context *ctdb, struct ctdb_req
 /*
   this is called in the client, when data comes in from the daemon
  */
-static void ctdb_client_read_cb(uint8_t *data, size_t cnt, void *args)
+void ctdb_client_read_cb(uint8_t *data, size_t cnt, void *args)
 {
        struct ctdb_context *ctdb = talloc_get_type(args, struct ctdb_context);
        struct ctdb_req_header *hdr = (struct ctdb_req_header *)data;
@@ -1944,7 +1944,11 @@ static int ctdb_fetch_with_header_func(struct ctdb_call_info *call)
 /*
   attach to a specific database - client call
 */
-struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name, bool persistent, uint32_t tdb_flags)
+struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb,
+                                   struct timeval timeout,
+                                   const char *name,
+                                   bool persistent,
+                                   uint32_t tdb_flags)
 {
        struct ctdb_db_context *ctdb_db;
        TDB_DATA data;
@@ -1979,7 +1983,7 @@ struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name,
        ctdb_db->db_id = *(uint32_t *)data.dptr;
        talloc_free(data.dptr);
 
-       ret = ctdb_ctrl_getdbpath(ctdb, timeval_current_ofs(2, 0), CTDB_CURRENT_NODE, ctdb_db->db_id, ctdb_db, &ctdb_db->db_path);
+       ret = ctdb_ctrl_getdbpath(ctdb, timeout, CTDB_CURRENT_NODE, ctdb_db->db_id, ctdb_db, &ctdb_db->db_path);
        if (ret != 0) {
                DEBUG(DEBUG_ERR,("Failed to get dbpath for database '%s'\n", name));
                talloc_free(ctdb_db);
index 145a13a81b7be3b2b97dc3b2d2e5fa8e02793650..5c00763ed7705ee52a63f53b3aef972225210b58 100644 (file)
@@ -111,7 +111,8 @@ struct ctdb_context *ctdb_cmdline_init(struct event_context *ev)
 /*
   startup a client only ctdb context
  */
-struct ctdb_context *ctdb_cmdline_client(struct event_context *ev)
+struct ctdb_context *ctdb_cmdline_client(struct tevent_context *ev,
+                                        struct timeval req_timeout)
 {
        struct ctdb_context *ctdb;
        char *socket_name;
@@ -152,7 +153,7 @@ struct ctdb_context *ctdb_cmdline_client(struct event_context *ev)
        }
 
        /* get our pnn */
-       ctdb->pnn = ctdb_ctrl_getpnn(ctdb, timeval_current_ofs(3, 0), CTDB_CURRENT_NODE);
+       ctdb->pnn = ctdb_ctrl_getpnn(ctdb, req_timeout, CTDB_CURRENT_NODE);
        if (ctdb->pnn == (uint32_t)-1) {
                DEBUG(DEBUG_CRIT,(__location__ " Failed to get ctdb pnn\n"));
                talloc_free(ctdb);
index 5fe54130d423595d34523c71a120a4a20d850093..1404a8290138ef3736a4e7d90c5e32024cd3352a 100644 (file)
@@ -357,4 +357,8 @@ int ctdb_sys_read_tcp_packet(int s, void *private_data,
 }
 
 
+bool ctdb_sys_check_iface_exists(const char *iface)
+{
+       return true;
+}
 
index f28045f95e5362a864ade4dcfa64df4c0886e83e..3fe90e6ef39079fb49a0d780f5cfae3d99733308 100644 (file)
@@ -73,3 +73,5 @@ bool ctdb_sys_have_ip(ctdb_sock_addr *_addr)
        close(s);
        return ret == 0;
 }
+
+
index a498ab2d686999f6b596d526e2b3f8736f1ef64f..2dcdffb3ece1c342c052214b8202fd171bb97d72 100644 (file)
@@ -537,3 +537,25 @@ int ctdb_sys_read_tcp_packet(int s, void *private_data,
 }
 
 
+bool ctdb_sys_check_iface_exists(const char *iface)
+{
+       int s;
+       struct ifreq ifr;
+
+       s = socket(PF_PACKET, SOCK_RAW, 0);
+       if (s == -1){
+               /* We dont know if the interface exists, so assume yes */
+               DEBUG(DEBUG_CRIT,(__location__ " failed to open raw socket\n"));
+               return true;
+       }
+
+       strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+       if (ioctl(s, SIOCGIFINDEX, &ifr) < 0 && errno == ENODEV) {
+               DEBUG(DEBUG_CRIT,(__location__ " interface '%s' not found\n", iface));
+               close(s);
+               return false;
+       }
+       close(s);
+       
+       return true;
+}
index 51150946730c61916151849df7882b47592826bc..163a0dd6ab599d58ad68faf837b73711dae3549b 100644 (file)
@@ -295,6 +295,10 @@ CTDB_DEBUGLEVEL=ERR
 # above 90%
 # CTDB_CHECK_FS_USE="/:90 /var:90"
 
+# Should CTDB automatically start and stop services when it is told to
+# newly manage or no longer manage them?
+CTDB_SERVICE_AUTOSTARTSTOP=yes
+
 # 
 #
 # set any default tuning options for ctdb
index 321ba9c708836bb345022471cd6fc3f4cf56fe44..2a48afb8e2063a420531ccf07bbcc874914cb499 100755 (executable)
@@ -35,6 +35,8 @@ update_config_from_tdb() {
     fi
 }
 
+ctdb_check_args "$@"
+
 case "$1" in 
      init)
         # make sure we have a blank state directory for the scripts to work with
index f75e6afea4e90435af1ce44337361c0c0e3d07f5..356ee08245f57e55ecf931e3af2fcbebb92b9f40 100755 (executable)
@@ -129,6 +129,8 @@ monitor_interfaces()
        return 1
 }
 
+ctdb_check_args "$@"
+
 case "$1" in 
      #############################
      # called when ctdbd starts up
@@ -158,10 +160,6 @@ case "$1" in
      ################################################
      # called when ctdbd wants to claim an IP address
      takeip)
-       if [ $# != 4 ]; then
-          echo "ERROR: must supply interface, IP and maskbits"
-          exit 1
-       fi
        iface=$2
        ip=$3
        maskbits=$4
@@ -181,11 +179,6 @@ case "$1" in
      ##################################################
      # called when ctdbd wants to release an IP address
      releaseip)
-       if [ $# != 4 ]; then
-          echo "ERROR: must supply interface, IP and maskbits"
-          exit 1
-       fi
-
        # releasing an IP is a bit more complex than it seems. Once the IP
        # is released, any open tcp connections to that IP on this host will end
        # up being stuck. Some of them (such as NFS connections) will be unkillable
@@ -220,11 +213,6 @@ case "$1" in
      ##################################################
      # called when ctdbd wants to update an IP address
      updateip)
-       if [ $# != 5 ]; then
-          echo "ERROR: must supply old interface, new interface, IP and maskbits"
-          exit 1
-       fi
-
        # moving an IP is a bit more complex than it seems.
        # First we drop all traffic on the old interface.
        # Then we try to add the ip to the new interface and before
index ff4dbdb8c37c3b162ac75fc721fd2b77ba835127..d8a064493e414cd6a504462f0936e44cb4ae161a 100755 (executable)
@@ -326,6 +326,8 @@ setup_per_ip_routing()
        return $?;
 }
 
+ctdb_check_args "$@"
+
 case "$1" in
      #############################
      # called when ctdbd starts up
@@ -368,10 +370,6 @@ case "$1" in
      ################################################
      # called when ctdbd wants to claim an IP address
      takeip)
-       if [ $# != 4 ]; then
-          echo "must supply interface, IP and maskbits"
-          exit 1
-       fi
        iface=$2
        ip=$3
        maskbits=$4
@@ -410,10 +408,6 @@ case "$1" in
      ################################################
      # called when ctdbd wants to claim an IP address
      updateip)
-       if [ $# != 5 ]; then
-          echo "must supply old interface, new interface, IP and maskbits"
-          exit 1
-       fi
        oiface=$2
        niface=$3
        ip=$4
@@ -455,11 +449,6 @@ case "$1" in
      ##################################################
      # called when ctdbd wants to release an IP address
      releaseip)
-       if [ $# != 4 ]; then
-          echo "must supply interface, IP and maskbits"
-          exit 1
-       fi
-
        iface=$2
        ip=$3
        maskbits=$4
index 87a91df5b5bb96eefd0637dc0339a39be4d333aa..e8ac61fe4319356f02f4807103e787fec36a43e0 100755 (executable)
@@ -70,6 +70,7 @@ case "$1" in
        [ "$CTDB_NFS_SKIP_SHARE_CHECK" = "yes" ] || {
            exportfs -v | grep '^/' | 
            sed -r -e 's@[[:space:]]+[^[:space:]()]+\([^[:space:]()]+\)$@@' | 
+           sort -u | 
            ctdb_check_directories 
        } || exit $?
 
index f65690359b303651f5b44aad7d9f615d42836a75..5ca7d8e693e8bdbeb2d8661266dcd89083f9cdbe 100755 (executable)
@@ -82,6 +82,26 @@ debug ()
     fi
 }
 
+##############################################################
+# check number of args for different events
+ctdb_check_args ()
+{
+    case "$1" in
+       takeip|releaseip)
+           if [ $# != 4 ]; then
+               echo "ERROR: must supply interface, IP and maskbits"
+               exit 1
+           fi
+           ;;
+       updateip)
+           if [ $# != 5 ]; then
+               echo "ERROR: must supply old interface, new interface, IP and maskbits"
+               exit 1
+           fi
+           ;;
+    esac
+}
+
 ##############################################################
 # determine on what type of system (init style) we are running
 detect_init_style() {
@@ -358,6 +378,7 @@ ctdb_check_tcp_ports()
        ctdb_check_tcp_ports_$_c "$@"
        case "$?" in
            0)
+               _ctdb_check_tcp_common
                rm -f "$_ctdb_service_started_file"
                return 0
                ;;
@@ -466,7 +487,8 @@ ctdb_check_tcp_ports_ctdb ()
     for _p ; do  # process each function argument (port)
        _cmd="ctdb checktcpport $_p"
        _out=$($_cmd 2>&1)
-       case "$?" in
+       _ret=$?
+       case "$_ret" in
            0)
                ctdb_check_tcp_ports_debug="\"$_cmd\" was able to bind to port"
                return 1
@@ -476,7 +498,7 @@ ctdb_check_tcp_ports_ctdb ()
                continue
                ;;
            *)
-               ctdb_check_tcp_ports_debug="$_cmd (exited with $?) with output:
+               ctdb_check_tcp_ports_debug="$_cmd (exited with $_ret) with output:
 $_out"
                # assume not implemented
                return 127
@@ -992,7 +1014,7 @@ ctdb_service_unset_reconfigure ()
 
 ctdb_service_reconfigure ()
 {
-    echo "Reconfiguring service \"$service_name\"..."
+    echo "Reconfiguring service \"$@\"..."
     ctdb_service_unset_reconfigure "$@"
     service_reconfigure "$@" || return $?
     ctdb_counter_init "$@"
@@ -1004,28 +1026,129 @@ service_reconfigure ()
     service "${1:-$service_name}" restart
 }
 
-ctdb_service_check_reconfigure ()
+ctdb_reconfigure_try_lock ()
 {
-    # Only do this for certain events.
-    case "$event_name" in
-       monitor|ipreallocated) : ;;
-       *) return 0
+    
+    _ctdb_service_reconfigure_common "$@"
+    _lock="${_d}/reconfigure_lock"
+    touch "$_lock"
+
+    (
+       flock 0
+       # This is overkill but will work if we need to extend this to
+       # allow certain events to run multiple times in parallel
+       # (e.g. takeip) and write multiple PIDs to the file.
+       read _locker_event 
+       if [ -n "$_locker_event" ] ; then
+           while read _pid ; do
+               if [ -n "$_pid" -a "$_pid" != $$ ] && \
+                   kill -0 "$_pid" 2>/dev/null ; then
+                   exit 1
+               fi
+           done
+       fi
+
+       printf "%s\n%s\n" "$event_name" $$ >"$_lock"
+       exit 0
+    ) <"$_lock"
+}
+
+ctdb_replay_monitor_status ()
+{
+    echo "Replaying previous status for this script due to reconfigure..."
+    # Leading colon (':') is missing in some versions...
+    _out=$(ctdb scriptstatus -Y | grep -E "^:?monitor:${script_name}:")
+    # Output looks like this:
+    # :monitor:60.nfs:1:ERROR:1314764004.030861:1314764004.035514:foo bar:
+    # This is the cheapest way of getting fields in the middle.
+    set -- $(IFS=":" ; echo $_out)
+    _code="$3"
+    _status="$4"
+    # The error output field can include colons so we'll try to
+    # preserve them.  The weak checking at the beginning tries to make
+    # this work for both broken (no leading ':') and fixed output.
+    _out="${_out%:}"
+    _err_out="${_out#*monitor:${script_name}:*:*:*:*:}"
+    case "$_status" in
+       OK) : ;;  # Do nothing special.
+       TIMEDOUT)
+           # Recast this as an error, since we can't exit with the
+           # correct negative number.
+           _code=1
+           _err_out="[Replay of TIMEDOUT scriptstatus - note incorrect return code.] ${_err_out}"
+           ;;
+       DISABLED)
+           # Recast this as an OK, since we can't exit with the
+           # correct negative number.
+           _code=0
+           _err_out="[Replay of DISABLED scriptstatus - note incorrect return code.] ${_err_out}"
+           ;;
+       *) : ;;  # Must be ERROR, do nothing special.
     esac
+    echo "$_err_out"
+    exit $_code
+}
 
-    if ctdb_service_needs_reconfigure "$@" ; then
-       ctdb_service_reconfigure "$@"
+ctdb_service_check_reconfigure ()
+{
+    [ -n "$1" ] || set -- "$service_name"
 
-       # Fall through to non-monitor events.
-       [ "$event_name" = "monitor" ] || return 0
+    # We only care about some events in this function.  For others we
+    # return now.
+    case "$event_name" in
+       monitor|ipreallocated|reconfigure) : ;;
+       *) return 0 ;;
+    esac
 
-       # We don't want to proceed with the rest of the monitor event
-       # here, so we exit.  However, if we exit 0 then, if the
-       # service was previously broken, we might return a false
-       # positive.  So we simply retrieve the status of this script
-       # from the previous monitor loop and exit with that status.
-       ctdb scriptstatus | \
-           grep -q -E "^${script_name}[[:space:]]+Status:OK[[:space:]]"
-       exit $?
+    if ctdb_reconfigure_try_lock "$@" ; then
+       # No events covered by this function are running, so proceed
+       # with gay abandon.
+       case "$event_name" in
+           reconfigure)
+               (ctdb_service_reconfigure "$@")
+               exit $?
+               ;;
+           ipreallocated)
+               if ctdb_service_needs_reconfigure "$@" ; then
+                   ctdb_service_reconfigure "$@"
+               fi
+               ;;
+           monitor)
+               if ctdb_service_needs_reconfigure "$@" ; then
+                   ctdb_service_reconfigure "$@"
+                   # Given that the reconfigure might not have
+                   # resulted in the service being stable yet, we
+                   # replay the previous status since that's the best
+                   # information we have.
+                   ctdb_replay_monitor_status
+               fi
+               ;;
+       esac
+    else
+       # Somebody else is running an event we don't want to collide
+       # with.  We proceed with caution.
+       case "$event_name" in
+           reconfigure)
+               # Tell whoever called us to retry.
+               exit 2
+               ;;
+           ipreallocated)
+               # Defer any scheduled reconfigure and just run the
+               # rest of the ipreallocated event, as per the
+               # eventscript.  There's an assumption here that the
+               # event doesn't depend on any scheduled reconfigure.
+               # This is true in the current code.
+               return 0
+               ;;
+           monitor)
+               # There is most likely a reconfigure in progress so
+               # the service is possibly unstable.  As above, we
+               # defer any scheduled reconfigured.  We also replay
+               # the previous monitor status since that's the best
+               # information we have.
+               ctdb_replay_monitor_status
+               ;;
+       esac
     fi
 }
 
@@ -1072,6 +1195,9 @@ is_ctdb_managed_service ()
 
 ctdb_start_stop_service ()
 {
+    # Do nothing unless configured to...
+    [ "$CTDB_SERVICE_AUTOSTARTSTOP" = "yes" ] || return 0
+
     _service_name="${1:-${service_name}}"
 
     [ "$event_name" = "monitor" ] || return 0
index cc3a1143daa743d3d465a2863a8a2b386460245a..adcc5dadad98939b999f4b345e26b2c6fe78a545 100644 (file)
@@ -64,6 +64,15 @@ m4_include(lib/util/signal.m4)
 m4_include(lib/util/fault.m4)
 
 AC_CHECK_HEADERS(sched.h)
+AC_CHECK_HEADERS(pcp/pmapi.h pcp/impl.h pcp/pmda.h, [], [],
+[[#ifdef HAVE_PCP_PMAPI_H
+# include <pcp/pmapi.h>
+#endif
+#ifdef HAVE_PCP_IMPL_H
+# include <pcp/impl.h>
+#endif
+]])
+
 AC_CHECK_FUNCS(sched_setscheduler)
 AC_CHECK_FUNCS(mlockall)
 
@@ -77,9 +86,20 @@ if test x"$ctdb_cv_HAVE_SOCK_SIN_LEN" = x"yes"; then
     AC_DEFINE(HAVE_SOCK_SIN_LEN,1,[Whether the sockaddr_in struct has a sin_len property])
 fi
 
+if test x"$ac_cv_header_pcp_pmda_h" = x"yes"; then
+    CTDB_PMDA=bin/pmdactdb
+    CTDB_PMDA_INSTALL=install_pmda
+else
+    CTDB_PMDA=
+    CTDB_PMDA_INSTALL=
+fi
+
+
 AC_SUBST(EXTRA_OBJ)
 AC_SUBST(CTDB_SYSTEM_OBJ)
 AC_SUBST(CTDB_SCSI_IO)
 AC_SUBST(CTDB_PCAP_LDFLAGS)
+AC_SUBST(CTDB_PMDA)
+AC_SUBST(CTDB_PMDA_INSTALL)
 
 AC_OUTPUT(Makefile ctdb.pc)
index 484902fb8359be85920b862981750fac27e0f34e..4cdbc0f76a84c856728ca8c21151f879071ee666 100644 (file)
@@ -115,6 +115,45 @@ void ctdb_disconnect(struct ctdb_connection *ctdb);
  *
  ***/
 
+/**
+ * ctdb_num_active - get the number of active commands
+ * @ctdb: the ctdb_connection from ctdb_connect.
+ *
+ * This command can be used to find the number of active commands we have
+ * issued. An active command is a command we have queued, or sent
+ * to the ctdb daemon but which we have not yet received a reply to.
+ *
+ * See Also:
+ *     ctdb_num_in_flight(), ctdb_num_out_queue()
+ */
+int ctdb_num_active(struct ctdb_connection *ctdb);
+
+/**
+ * ctdb_num_in_flight - get the number of commands in flight.
+ * @ctdb: the ctdb_connection from ctdb_connect.
+ *
+ * This command can be used to find the number of commands we have
+ * sent to the ctdb daemon to which we have not yet received/processed
+ * the reply.
+ *
+ * See Also:
+ *     ctdb_num_out_queue(), ctdb_num_active()
+ */
+int ctdb_num_in_flight(struct ctdb_connection *ctdb);
+
+/**
+ * ctdb_num_out_queue - get the number of commands in the out queue
+ * @ctdb: the ctdb_connection from ctdb_connect.
+ *
+ * This command can be used to find the number of commands we have
+ * queued for delivery to the ctdb daemon but have not yet been
+ * written to the domain socket.
+ *
+ * See Also:
+ *     ctdb_num_in_flight(), ctdb_num_active()
+ */
+int ctdb_num_out_queue(struct ctdb_connection *ctdb);
+
 /**
  * ctdb_get_fd - get the filedescriptor to select/poll on
  * @ctdb: the ctdb_connection from ctdb_connect.
index ad31d24b317c6bf5eff285c07c96c9d42054f8b7..01219f341b6a8ba76e6e0106d3a6a3681bb738c2 100644 (file)
@@ -105,7 +105,12 @@ int ctdb_start_daemon(struct ctdb_context *ctdb, bool do_fork, bool use_syslog,
 /*
   attach to a ctdb database
 */
-struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb, const char *name, bool persistent, uint32_t tdb_flags);
+struct ctdb_db_context *ctdb_attach(struct ctdb_context *ctdb,
+                                   struct timeval timeout,
+                                   const char *name,
+                                   bool persistent,
+                                   uint32_t tdb_flags);
+
 
 /*
   find an attached ctdb_db handle given a name
@@ -190,7 +195,8 @@ int ctdb_register_message_handler(struct ctdb_context *ctdb,
 struct ctdb_db_context *find_ctdb_db(struct ctdb_context *ctdb, uint32_t id);
 
 
-struct ctdb_context *ctdb_cmdline_client(struct tevent_context *ev);
+struct ctdb_context *ctdb_cmdline_client(struct tevent_context *ev,
+                                        struct timeval req_timeout);
 
 struct ctdb_statistics;
 int ctdb_ctrl_statistics(struct ctdb_context *ctdb, uint32_t destnode, struct ctdb_statistics *status);
index 6a8ed187efc7424060b7c3e04f31d13fee7f8e9d..b093bf09acbc1f63eef0e8a0bcad38ad0c144023 100644 (file)
@@ -121,6 +121,7 @@ struct ctdb_tunable {
        uint32_t deferred_attach_timeout;
        uint32_t vacuum_fast_path_count;
        uint32_t lcp2_public_ip_assignment;
+       uint32_t allow_client_db_attach;
 };
 
 /*
@@ -413,6 +414,7 @@ struct ctdb_context {
        uint32_t recovery_mode;
        TALLOC_CTX *tickle_update_context;
        TALLOC_CTX *keepalive_ctx;
+       TALLOC_CTX *check_public_ifaces_ctx;
        struct ctdb_tunable tunable;
        enum ctdb_freeze_mode freeze_mode[NUM_DB_PRIORITIES+1];
        struct ctdb_freeze_handle *freeze_handles[NUM_DB_PRIORITIES+1];
@@ -802,6 +804,7 @@ int ctdb_call_local(struct ctdb_db_context *ctdb_db, struct ctdb_call *call,
 void ctdb_recv_raw_pkt(void *p, uint8_t *data, uint32_t length);
 
 int ctdb_socket_connect(struct ctdb_context *ctdb);
+void ctdb_client_read_cb(uint8_t *data, size_t cnt, void *args);
 
 #define CTDB_BAD_REQID ((uint32_t)-1)
 uint32_t ctdb_reqid_new(struct ctdb_context *ctdb, void *state);
@@ -1116,6 +1119,7 @@ int ctdb_ctrl_set_iface_link(struct ctdb_context *ctdb,
 uint32_t uint16_checksum(uint16_t *data, size_t n);
 int ctdb_sys_send_arp(const ctdb_sock_addr *addr, const char *iface);
 bool ctdb_sys_have_ip(ctdb_sock_addr *addr);
+bool ctdb_sys_check_iface_exists(const char *iface);
 int ctdb_sys_send_tcp(const ctdb_sock_addr *dest, 
                      const ctdb_sock_addr *src,
                      uint32_t seq, uint32_t ack, int rst);
@@ -1358,6 +1362,7 @@ int32_t ctdb_control_clear_log(struct ctdb_context *ctdb);
 
 struct ctdb_log_state *ctdb_fork_with_logging(TALLOC_CTX *mem_ctx,
                                              struct ctdb_context *ctdb,
+                                             const char *log_prefix,
                                              void (*logfn)(const char *, uint16_t, void *),
                                              void *logfn_private, pid_t *pid);
 
index 28c9da346aab0420e2076db02e0fd56372ade40c..54c1787400f78c711e33555681d941b7911527a6 100644 (file)
@@ -107,7 +107,7 @@ void tevent_before_wait(struct event_context *ev) {
        if (!tevent_timeval_is_zero(&tevent_after_wait_ts)) {
                diff = tevent_timeval_until(&tevent_after_wait_ts, &now);
                if (diff.tv_sec > 3) {
-                       tevent_debug(ev, TEVENT_DEBUG_FATAL,  __location__ 
+                       tevent_debug(ev, TEVENT_DEBUG_ERROR,  __location__ 
                                     " Handling event took %d seconds!",
                                     (int) diff.tv_sec);
                }
index 98c86f600db60e6d317eb85e1d17761beddc25e5..72841dbd7462f8b67ea770ef64f85d6e3478ba8e 100644 (file)
@@ -1134,3 +1134,32 @@ bool ctdb_traverse_async(struct ctdb_db *ctdb_db,
 
        return true;
 }
+
+int ctdb_num_out_queue(struct ctdb_connection *ctdb)
+{
+       struct ctdb_request *req;
+       int i;
+
+       for (i = 0, req = ctdb->outq; req; req = req->next, i++)
+               ;
+
+       return i;
+}
+
+int ctdb_num_in_flight(struct ctdb_connection *ctdb)
+{
+       struct ctdb_request *req;
+       int i;
+
+       for (i = 0, req = ctdb->doneq; req; req = req->next, i++)
+               ;
+
+       return i;
+}
+
+int ctdb_num_active(struct ctdb_connection *ctdb)
+{
+       return ctdb_num_out_queue(ctdb)
+                + ctdb_num_in_flight(ctdb);
+}
+
index f618e76013f5b562f5a376149ecd6039d05cdb16..4b2d98b3b55ec150bd60d0f9fd98bc7298328d45 100644 (file)
@@ -146,6 +146,8 @@ development libraries for ctdb
 %{_libdir}/libctdb.a
 
 %changelog
+* Thu Sep 1 2011 : Version 1.11
+ - Major updates.
 * Tue May 25 2010 : Version 1.10
  - New version 1.10
 * Tue May 25 2010 : Version 1.9
index 27b990ef6768b09967b4e8139f0bbf19f9d1f5d8..2ef2a0b1fb8480e9a3bc1e27fd983bc8409f5e3e 100644 (file)
@@ -163,6 +163,7 @@ int start_syslog_daemon(struct ctdb_context *ctdb)
 
 struct ctdb_log_state {
        struct ctdb_context *ctdb;
+       const char *prefix;
        int fd, pfd;
        char buf[1024];
        uint16_t buf_used;
@@ -366,7 +367,11 @@ static void write_to_log(struct ctdb_log_state *log,
                         const char *buf, unsigned int len)
 {
        if (script_log_level <= LogLevel) {
-               do_debug("%*.*s\n", len, len, buf);
+               if (log != NULL && log->prefix != NULL) {
+                       do_debug("%s: %*.*s\n", log->prefix, len, len, buf);
+               } else {
+                       do_debug("%*.*s\n", len, len, buf);
+               }
                /* log it in the eventsystem as well */
                if (log->logfn)
                        log->logfn(log->buf, len, log->logfn_private);
@@ -386,7 +391,7 @@ static void ctdb_log_handler(struct event_context *ev, struct fd_event *fde,
        if (!(flags & EVENT_FD_READ)) {
                return;
        }
-       
+
        n = read(log->pfd, &log->buf[log->buf_used],
                 sizeof(log->buf) - log->buf_used);
        if (n > 0) {
@@ -436,6 +441,7 @@ static int log_context_destructor(struct ctdb_log_state *log)
 */
 struct ctdb_log_state *ctdb_fork_with_logging(TALLOC_CTX *mem_ctx,
                                              struct ctdb_context *ctdb,
+                                             const char *log_prefix,
                                              void (*logfn)(const char *, uint16_t, void *),
                                              void *logfn_private, pid_t *pid)
 {
@@ -446,6 +452,7 @@ struct ctdb_log_state *ctdb_fork_with_logging(TALLOC_CTX *mem_ctx,
        log = talloc_zero(mem_ctx, struct ctdb_log_state);
        CTDB_NO_MEMORY_NULL(ctdb, log);
        log->ctdb = ctdb;
+       log->prefix = log_prefix;
        log->logfn = logfn;
        log->logfn_private = (void *)logfn_private;
 
index 555d949ece7e265d918ec6d644977910b54dede9..b76ae6a6221669d25a4c93d2fc366b97d9f2ad63 100644 (file)
@@ -1080,6 +1080,12 @@ int32_t ctdb_control_db_attach(struct ctdb_context *ctdb, TDB_DATA indata,
        struct ctdb_node *node = ctdb->nodes[ctdb->pnn];
        struct ctdb_client *client = NULL;
 
+       if (ctdb->tunable.allow_client_db_attach == 0) {
+               DEBUG(DEBUG_ERR, ("DB Attach to database %s denied by tunable "
+                                 "AllowClientDBAccess == 0\n", db_name));
+               return -1;
+       }
+
        /* dont allow any local clients to attach while we are in recovery mode
         * except for the recovery daemon.
         * allow all attach from the network since these are always from remote
index 44a9e4bfad08f921d39835173cf26978c2431e21..631f53e89bd4d04908b4b3abc8b1da761d86163a 100644 (file)
@@ -842,7 +842,7 @@ static void vacuum_fetch_handler(struct ctdb_context *ctdb, uint64_t srvid,
        }
 
        /* attach to it */
-       ctdb_db = ctdb_attach(ctdb, name, persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, CONTROL_TIMEOUT(), name, persistent, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,(__location__ " Failed to attach to database '%s'\n", name));
                talloc_free(tmp_ctx);
index 1e2e829e4dbbed1b56cdaae0fcdf294a54fe1307..4114b40f83e0ccb76da575a39d3e1a1ed8d70761 100644 (file)
@@ -880,6 +880,16 @@ static int ctdb_add_public_address(struct ctdb_context *ctdb,
        int i;
        int ret;
 
+       tmp = talloc_strdup(vnn, ifaces);
+       for (iface = strtok(tmp, ","); iface; iface = strtok(NULL, ",")) {
+               if (!ctdb_sys_check_iface_exists(iface)) {
+                       DEBUG(DEBUG_CRIT,("Interface %s does not exist. Can not add public-address : %s\n", iface, ctdb_addr_to_str(addr)));
+                       talloc_free(tmp);
+                       return -1;
+               }
+       }
+       talloc_free(tmp);
+
        /* Verify that we dont have an entry for this ip yet */
        for (vnn=ctdb->vnn;vnn;vnn=vnn->next) {
                if (ctdb_same_sockaddr(addr, &vnn->public_address)) {
@@ -941,6 +951,51 @@ int ctdb_set_event_script_dir(struct ctdb_context *ctdb, const char *script_dir)
        return 0;
 }
 
+static void ctdb_check_interfaces_event(struct event_context *ev, struct timed_event *te, 
+                                 struct timeval t, void *private_data)
+{
+       struct ctdb_context *ctdb = talloc_get_type(private_data, 
+                                                       struct ctdb_context);
+       struct ctdb_vnn *vnn;
+
+       for (vnn=ctdb->vnn;vnn;vnn=vnn->next) {
+               int i;
+
+               for (i=0; vnn->ifaces[i] != NULL; i++) {
+                       if (!ctdb_sys_check_iface_exists(vnn->ifaces[i])) {
+                               DEBUG(DEBUG_CRIT,("Interface %s does not exist but is used by public ip %s\n",
+                                       vnn->ifaces[i],
+                                       ctdb_addr_to_str(&vnn->public_address)));
+                       }
+               }
+       }
+
+       event_add_timed(ctdb->ev, ctdb->check_public_ifaces_ctx, 
+               timeval_current_ofs(30, 0), 
+               ctdb_check_interfaces_event, ctdb);
+}
+
+
+static int ctdb_start_monitoring_interfaces(struct ctdb_context *ctdb)
+{
+       if (ctdb->check_public_ifaces_ctx != NULL) {
+               talloc_free(ctdb->check_public_ifaces_ctx);
+               ctdb->check_public_ifaces_ctx = NULL;
+       }
+
+       ctdb->check_public_ifaces_ctx = talloc_new(ctdb);
+       if (ctdb->check_public_ifaces_ctx == NULL) {
+               ctdb_fatal(ctdb, "failed to allocate context for checking interfaces");
+       }
+
+       event_add_timed(ctdb->ev, ctdb->check_public_ifaces_ctx, 
+               timeval_current_ofs(30, 0), 
+               ctdb_check_interfaces_event, ctdb);
+
+       return 0;
+}
+
+
 /*
   setup the public address lists from a file
 */
@@ -1003,6 +1058,9 @@ int ctdb_set_public_addresses(struct ctdb_context *ctdb, const char *alist)
                }
        }
 
+
+       ctdb_start_monitoring_interfaces(ctdb);
+
        talloc_free(lines);
        return 0;
 }
index 9da3cc806562f1601797ecf040b0a5762a6de39c..ef86051cecf2ebd634dfa30b5c4d28dca4d9b1f3 100644 (file)
@@ -68,7 +68,8 @@ static const struct {
        { "UseStatusEvents",     0,  offsetof(struct ctdb_tunable, use_status_events_for_monitoring) },
        { "AllowUnhealthyDBRead", 0,  offsetof(struct ctdb_tunable, allow_unhealthy_db_read) },
        { "StatHistoryInterval",  1,  offsetof(struct ctdb_tunable, stat_history_interval) },
-       { "DeferredAttachTO",  120,  offsetof(struct ctdb_tunable, deferred_attach_timeout) }
+       { "DeferredAttachTO",  120,  offsetof(struct ctdb_tunable, deferred_attach_timeout) },
+       { "AllowClientDBAttach", 1, offsetof(struct ctdb_tunable, allow_client_db_attach) }
 };
 
 /*
index eb5c2da2fe7f0197f7bec7560c861c25c0f27258..c01fbad69ed8129efd53937f224ca590fd5fb4f5 100644 (file)
@@ -382,7 +382,7 @@ static int fork_child_for_script(struct ctdb_context *ctdb,
                return -errno;
        }
 
-       if (!ctdb_fork_with_logging(state, ctdb, log_event_script_output,
+       if (!ctdb_fork_with_logging(state, ctdb, current->name, log_event_script_output,
                                    state, &state->child)) {
                r = -errno;
                close(state->fd[0]);
index ab7abb9faca9449cc4adcf4a18fc91000cd90ae0..9003b396c200b2672be7a420ef51fb229dcfd2e2 100644 (file)
@@ -1,5 +1,8 @@
 # Hey Emacs, this is a -*- shell-script -*- !!!  :-)
 
+# Print a message and exit.
+die () { echo "$@" >&2 ; exit 1 ; }
+
 # Augment PATH with relevant stubs/ directories.  We do this by actually
 # setting PATH, and also by setting $EVENTSCRIPTS_PATH and then
 # prepending that to $PATH in rc.local to avoid the PATH reset in
@@ -30,8 +33,7 @@ if [ -d "${EVENTSCRIPTS_TESTCASE_DIR}/etc" ] ; then
 elif [ -d "${EVENTSCRIPTS_TESTS_DIR}/etc" ] ; then
     CTDB_ETCDIR="${EVENTSCRIPTS_TESTS_DIR}/etc"
 else
-    echo "Unable to set \$CTDB_ETCDIR" >&2
-    exit 1
+    die "Unable to set \$CTDB_ETCDIR"
 fi
 export CTDB_ETCDIR
 
@@ -42,8 +44,7 @@ elif [ -d "${EVENTSCRIPTS_TESTCASE_DIR}/etc/ctdb" ] ; then
 elif [ -d "${EVENTSCRIPTS_TESTS_DIR}/etc-ctdb" ] ; then
     CTDB_BASE="${EVENTSCRIPTS_TESTS_DIR}/etc-ctdb"
 else
-    echo "Unable to set \$CTDB_BASE" >&2
-    exit 1
+    die "Unable to set \$CTDB_BASE"
 fi
 export CTDB_BASE
 
@@ -109,7 +110,7 @@ tcp_port_down ()
 {
     for _i ; do
        debug "Marking TCP port \"${_i}\" as not listening"
-       FAKE_NETSTAT_TCP_LISTEN=$(echo "$FAKE_NETSTAT_TCP_LISTEN" | sed -r -e "s@[[:space:]]*[\.0-9]+:${_i}@@g")
+       FAKE_TCP_LISTEN=$(echo "$FAKE_TCP_LISTEN" | sed -r -e "s@[[:space:]]*[\.0-9]+:${_i}@@g")
     done
 }
 
@@ -177,6 +178,11 @@ ethtool_interfaces_up ()
     done
 }
 
+setup_nmap_output_filter ()
+{
+    OUT_FILTER="-e 's@^(DEBUG: # Nmap 5.21 scan initiated) .+ (as:)@\1 DATE \2@' -e 's@^(DEBUG: # Nmap done at) .+ (--)@\1 DATE \2@'"
+}
+
 ######################################################################
 
 # CTDB fakery
@@ -232,6 +238,10 @@ setup_ctdb ()
     export FAKE_CTDB_NODES_DOWN="$EVENTSCRIPTS_TESTS_VAR_DIR/nodes-down"
     mkdir -p "$FAKE_CTDB_NODES_DOWN"
     rm -f "$FAKE_CTDB_NODES_DOWN"/*
+
+    export FAKE_CTDB_SCRIPTSTATUS="$EVENTSCRIPTS_TESTS_VAR_DIR/scriptstatus"
+    mkdir -p "$FAKE_CTDB_SCRIPTSTATUS"
+    rm -f "$FAKE_CTDB_SCRIPTSTATUS"/*
 }
 
 
@@ -280,6 +290,25 @@ ctdb_get_1_public_address ()
     echo "${_addrs%% *}" | sed -r -e 's#(.*)/(.*)@(.*)#\3 \1 \2#g'
 }
 
+ctdb_not_implemented ()
+{
+    export CTDB_NOT_IMPLEMENTED="$1"
+    ctdb_not_implemented="\
+DEBUG: ctdb: command \"$1\" not implemented in stub"
+}
+
+ctdb_fake_scriptstatus ()
+{
+    _code="$1"
+    _status="$2"
+    _err_out="$3"
+
+    _d1=$(date '+%s.%N')
+    _d2=$(date '+%s.%N')
+
+    echo "$_code $_status $_err_out" >"$FAKE_CTDB_SCRIPTSTATUS/$script"
+}
+
 ######################################################################
 
 # Samba fakery
@@ -303,8 +332,13 @@ setup_samba ()
        export CTDB_SAMBA_SKIP_SHARE_CHECK="no"
        export CTDB_MANAGED_SERVICES="foo samba winbind bar"
 
-       export FAKE_NETSTAT_TCP_LISTEN="0.0.0.0:445 0.0.0.0:139"
+       export FAKE_TCP_LISTEN="0.0.0.0:445 0.0.0.0:139"
        export FAKE_WBINFO_FAIL="no"
+
+       # Some things in 50.samba are backgrounded and waited for.  If
+       # we don't sleep at all then timeouts can happen.  This avoids
+       # that...  :-)
+       export FAKE_SLEEP_FORCE=0.1
     else
        debug "Marking Samba services as down, not listening and not managed by CTDB"
         # Get into known state.
@@ -321,7 +355,7 @@ setup_samba ()
        unset CTDB_MANAGES_SAMBA
        unset CTDB_MANAGES_WINBIND
 
-       export FAKE_NETSTAT_TCP_LISTEN=""
+       export FAKE_TCP_LISTEN=""
        export FAKE_WBINFO_FAIL="yes"
     fi
 
@@ -403,9 +437,7 @@ rpc_services_up ()
            rquotad)  _t="1:2" ;;
            nlockmgr) _t="3:4" ;;
            status)   _t="1:1" ;;
-           *)
-               echo "Internal error - unsupported RPC service \"${_i}\""
-               exit 1
+           *) die "Internal error - unsupported RPC service \"${_i}\"" ;;
        esac
 
        FAKE_RPCINFO_SERVICES="${FAKE_RPCINFO_SERVICES}${FAKE_RPCINFO_SERVICES:+ }${_i}:${_t}"
@@ -533,8 +565,7 @@ Starting nfslock: OK"
 setup_vsftpd ()
 {
     if [ "$1" != "down" ] ; then
-       echo "setup_vsftpd up not implemented!!!"
-       exit 1
+       die "setup_vsftpd up not implemented!!!"
     else
        debug "Setting up VSFTPD environment: service down, not managed by CTDB"
 
@@ -553,8 +584,7 @@ setup_vsftpd ()
 setup_httpd ()
 {
     if [ "$1" != "down" ] ; then
-       echo "setup_httpd up not implemented!!!"
-       exit 1
+       die "setup_httpd up not implemented!!!"
     else
        debug "Setting up HTTPD environment: service down, not managed by CTDB"
 
@@ -578,16 +608,27 @@ define_test ()
     desc="$1"
 
     _f="$0"
-    _f="${_f#./}"      # strip leading ./
-    _f="${_f#simple/}" # strip leading simple/
-    _f="${_f%%/*}"     # if subdir, strip off file
-    _f="${_f%.sh}"     # strip off .sh suffix if any
+    _f="${_f#./}"          # strip leading ./
+    _f="${_f#simple/}"     # strip leading simple/
+    _f="${_f#multievent/}" # strip leading multievent/
+    _f="${_f%%/*}"         # if subdir, strip off file
+    _f="${_f%.sh}"         # strip off .sh suffix if any
 
-    # Remaining format should be NN.service.event.NNN:
+    # Remaining format should be NN.service.event.NNN or NN.service.NNN:
     _num="${_f##*.}"
     _f="${_f%.*}"
-    event="${_f##*.}"
-    script="${_f%.*}"
+    case "$_f" in
+       *.*.*)
+           script="${_f%.*}"
+           event="${_f##*.}"
+           ;;
+       *.*)
+           script="$_f"
+           unset event
+           ;;
+       *)
+           die "Internal error - unknown testcase filename format"
+    esac
 
     printf "%-14s %-10s %-4s - %s\n\n" "$script" "$event" "$_num" "$desc"
 }
@@ -637,25 +678,24 @@ result_print ()
        if [ -n "$_iteration" ] ; then
            cat <<EOF
 
-##################################################
-##################################################
+==================================================
 Iteration $_iteration
 EOF
        fi
 
 cat <<EOF
-##################################################
+--------------------------------------------------
 Output (Exit status: ${_rc}):
-##################################################
+--------------------------------------------------
 $_out
 EOF
     fi
 
     if ! $_passed ; then
        cat <<EOF
-##################################################
+--------------------------------------------------
 Required output (Exit status: ${required_rc}):
-##################################################
+--------------------------------------------------
 $required_output
 EOF
     fi
@@ -668,20 +708,19 @@ result_footer ()
     if [ "$EVENTSCRIPT_TESTS_VERBOSE" = "yes" ] || ! $_passed ; then
 
        cat <<EOF
-##################################################
+--------------------------------------------------
 CTDB_BASE="$CTDB_BASE"
 CTDB_ETCDIR="$CTDB_ETCDIR"
 ctdb client is "$(which ctdb)"
-##################################################
+--------------------------------------------------
 EOF
     fi
 
-    echo
-
     if $_passed ; then
        echo "PASSED"
        return 0
     else
+       echo
        echo "FAILED"
        return 1
     fi
@@ -699,11 +738,19 @@ EOF
 # useful for debugging.
 simple_test ()
 {
-    echo "Running \"${CTDB_BASE}/events.d/$script $event\""
+    [ -n "$event" ] || die 'simple_test: $event not set'
+
+    echo "Running \"$script $event${1:+ }$*\""
     _out=$($EVENTSCRIPTS_TESTS_TRACE "${CTDB_BASE}/events.d/$script" "$event" "$@" 2>&1)
     _rc=$?
 
-    if [ "$_out" = "$required_output" -a $_rc = $required_rc ] ; then
+    if [ -n "$OUT_FILTER" ] ; then
+       _fout=$(echo "$_out" | eval sed -r $OUT_FILTER)
+    else
+       _fout="$_out"
+    fi
+
+    if [ "$_fout" = "$required_output" -a $_rc = $required_rc ] ; then
        _passed=true
     else
        _passed=false
@@ -713,6 +760,17 @@ simple_test ()
     result_footer "$_passed"
 }
 
+simple_test_event ()
+{
+    # If something has previously failed then don't continue.
+    : ${_passed:=true}
+    $_passed || return 1
+
+    event="$1" ; shift
+    echo "##################################################"
+    simple_test "$@"
+}
+
 # Run an eventscript iteratively.
 # - 1st argument is the number of iterations.
 # - 2nd argument is something to eval to do setup for every iteration.
@@ -733,6 +791,8 @@ simple_test ()
 # iteration.
 iterate_test ()
 {
+    [ -n "$event" ] || die 'simple_test: $event not set'
+
     args=""
     if [ "$1" = "--" ] ; then
        shift
@@ -747,7 +807,7 @@ iterate_test ()
     _setup_default="$2"
     shift 2
 
-    echo "Running $_repeats iterations of \"${CTDB_BASE}/events.d/$script $event\" $args"
+    echo "Running $_repeats iterations of \"$script $event\" $args"
 
     _result=true
 
@@ -765,7 +825,13 @@ iterate_test ()
        _out=$($EVENTSCRIPTS_TESTS_TRACE "${CTDB_BASE}/events.d/$script" "$event" $args 2>&1)
        _rc=$?
 
-       if [ "$_out" = "$required_output" -a $_rc = $required_rc ] ; then
+    if [ -n "$OUT_FILTER" ] ; then
+       _fout=$(echo "$_out" | eval sed -r $OUT_FILTER)
+    else
+       _fout="$_out"
+    fi
+
+       if [ "$_fout" = "$required_output" -a $_rc = $required_rc ] ; then
            _passed=true
        else
            _passed=false
diff --git a/tests/eventscripts/etc/sysconfig/ctdb b/tests/eventscripts/etc/sysconfig/ctdb
new file mode 120000 (symlink)
index 0000000..1c6e2b9
--- /dev/null
@@ -0,0 +1 @@
+../../../../config/ctdb.sysconfig
\ No newline at end of file
diff --git a/tests/eventscripts/multievent/10.interface.001.sh b/tests/eventscripts/multievent/10.interface.001.sh
new file mode 100755 (executable)
index 0000000..5ef254c
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "takeip, removeip"
+
+setup_ctdb
+
+public_address=$(ctdb_get_1_public_address)
+
+ok_null
+
+simple_test_event "takeip" $public_address
+simple_test_event "releaseip" $public_address
diff --git a/tests/eventscripts/multievent/60.nfs.001.sh b/tests/eventscripts/multievent/60.nfs.001.sh
new file mode 100755 (executable)
index 0000000..a6608db
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "takeip, ipreallocated -> reconfigure"
+
+setup_nfs
+
+public_address=$(ctdb_get_1_public_address)
+
+ok_null
+
+simple_test_event "takeip" $public_address
+
+ok <<EOF
+Reconfiguring service "nfs"...
+Starting nfslock: OK
+Starting nfs: OK
+EOF
+
+simple_test_event "ipreallocated"
diff --git a/tests/eventscripts/multievent/60.nfs.002.sh b/tests/eventscripts/multievent/60.nfs.002.sh
new file mode 100755 (executable)
index 0000000..93f645a
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "takeip, monitor -> reconfigure"
+
+setup_nfs
+
+public_address=$(ctdb_get_1_public_address)
+
+ok_null
+
+simple_test_event "takeip" $public_address
+
+# This currently assumes that ctdb scriptstatus will always return a
+# good status (when replaying).  That should change and we will need
+# to split this into 2 tests.
+ok <<EOF
+Reconfiguring service "nfs"...
+Starting nfslock: OK
+Starting nfs: OK
+Replaying previous status for this script due to reconfigure...
+EOF
+
+simple_test_event "monitor"
diff --git a/tests/eventscripts/multievent/60.nfs.003.sh b/tests/eventscripts/multievent/60.nfs.003.sh
new file mode 100755 (executable)
index 0000000..929eaf2
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "takeip, monitor -> reconfigure, replay error"
+
+setup_nfs
+
+public_address=$(ctdb_get_1_public_address)
+
+err="foo: bar error occurred"
+
+ok_null
+
+simple_test_event "takeip" $public_address
+
+ctdb_fake_scriptstatus 1 "ERROR" "$err"
+
+required_result 1 <<EOF
+Reconfiguring service "nfs"...
+Starting nfslock: OK
+Starting nfs: OK
+Replaying previous status for this script due to reconfigure...
+$err
+EOF
+
+simple_test_event "monitor"
diff --git a/tests/eventscripts/multievent/60.nfs.004.sh b/tests/eventscripts/multievent/60.nfs.004.sh
new file mode 100755 (executable)
index 0000000..addbf44
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "takeip, monitor -> reconfigure, replay timedout"
+
+setup_nfs
+
+public_address=$(ctdb_get_1_public_address)
+
+err="waiting, waiting..."
+
+ok_null
+
+simple_test_event "takeip" $public_address
+
+ctdb_fake_scriptstatus -62 "TIMEDOUT" "$err"
+
+required_result 1 <<EOF
+Reconfiguring service "nfs"...
+Starting nfslock: OK
+Starting nfs: OK
+Replaying previous status for this script due to reconfigure...
+[Replay of TIMEDOUT scriptstatus - note incorrect return code.] $err
+EOF
+
+simple_test_event "monitor"
diff --git a/tests/eventscripts/multievent/60.nfs.005.sh b/tests/eventscripts/multievent/60.nfs.005.sh
new file mode 100755 (executable)
index 0000000..9e65201
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "takeip, monitor -> reconfigure, replay disabled"
+
+setup_nfs
+
+public_address=$(ctdb_get_1_public_address)
+
+err=""
+
+ok_null
+
+simple_test_event "takeip" $public_address
+
+ctdb_fake_scriptstatus -8 "DISABLED" "$err"
+
+ok <<EOF
+Reconfiguring service "nfs"...
+Starting nfslock: OK
+Starting nfs: OK
+Replaying previous status for this script due to reconfigure...
+[Replay of DISABLED scriptstatus - note incorrect return code.] $err
+EOF
+
+simple_test_event "monitor"
diff --git a/tests/eventscripts/multievent/60.nfs.006.sh b/tests/eventscripts/multievent/60.nfs.006.sh
new file mode 100755 (executable)
index 0000000..35f8b83
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "reconfigure (synthetic), twice"
+# This checks that the lock is released...
+
+setup_nfs
+
+public_address=$(ctdb_get_1_public_address)
+
+err=""
+
+ok <<EOF
+Reconfiguring service "nfs"...
+Starting nfslock: OK
+Starting nfs: OK
+EOF
+
+simple_test_event "reconfigure"
+simple_test_event "reconfigure"
index cb2c8f2950763c920e82f9e1fc5115d93261eea4..ec17b5f7a71a08eba52e6c5a9fbc141d4f80c076 100755 (executable)
@@ -15,6 +15,12 @@ for i ; do
            export EVENTSCRIPT_TESTS_VERBOSE="yes"
            shift
            ;;
+       -T)
+           # This will cause tests to fail but is good for debugging
+           # individual tests when they fail.
+           export EVENTSCRIPTS_TESTS_TRACE="sh -x"
+           shift
+           ;;
        -*)
            opts="$opts $i"
            shift
index fe2afd1d9fb0a443c4e86a7f72d8a1ec51a38e31..0f8b1a53c63eeda3678d3510b8a852c15780ff46 100755 (executable)
@@ -8,4 +8,4 @@ setup_vsftpd "down"
 
 ok_null
 
-simple_test $cmd
+simple_test
index c46a45c0c756529e101ffdde8639508ad4410382..0a9ceac428eecc87206dcef6c40a1b25d3100d0a 100755 (executable)
@@ -8,4 +8,4 @@ setup_httpd "down"
 
 ok_null
 
-simple_test $cmd
+simple_test
diff --git a/tests/eventscripts/simple/50.samba.monitor.107.sh b/tests/eventscripts/simple/50.samba.monitor.107.sh
new file mode 100755 (executable)
index 0000000..e4151f3
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "port 139 down, default tcp checker, debug"
+
+# This has to go before the setup, otherwise it will write a dud file.
+export CTDB_DEBUGLEVEL=4
+
+setup_samba
+tcp_port_down 139
+
+required_result 1 <<EOF
+ERROR: samba tcp port 139 is not responding
+DEBUG: "ctdb checktcpport 139" was able to bind to port
+EOF
+
+simple_test
diff --git a/tests/eventscripts/simple/50.samba.monitor.108.sh b/tests/eventscripts/simple/50.samba.monitor.108.sh
new file mode 100755 (executable)
index 0000000..d06a16f
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "port 139 down, ctdb checktcpport not implemented"
+
+# TODO: create nmap stub
+export CTDB_NOT_IMPLEMENTED="checktcpport"
+
+setup_samba
+tcp_port_down 139
+
+required_result 1 "ERROR: samba tcp port 139 is not responding"
+
+simple_test
diff --git a/tests/eventscripts/simple/50.samba.monitor.109.sh b/tests/eventscripts/simple/50.samba.monitor.109.sh
new file mode 100755 (executable)
index 0000000..4bc854b
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "port 139 down, ctdb checktcpport not implemented, debug"
+
+ctdb_not_implemented "checktcpport"
+
+# This has to go before the setup, otherwise it will write a dud file.
+export CTDB_DEBUGLEVEL=4
+
+setup_nmap_output_filter
+
+setup_samba
+tcp_port_down 139
+
+required_result 1 <<EOF
+DEBUG: ctdb_check_ports - checker ctdb not implemented
+DEBUG: output from checker was:
+DEBUG: ctdb checktcpport 445 (exited with 1) with output:
+$ctdb_not_implemented
+ERROR: samba tcp port 139 is not responding
+DEBUG: nmap -n -oG - -PS 127.0.0.1 -p 445,139 shows this output:
+DEBUG: # Nmap 5.21 scan initiated DATE as: nmap -n -oG - -PS 127.0.0.1 -p 445,139
+DEBUG: Host: 127.0.0.1 ()      Status: Up
+DEBUG: Host: 127.0.0.1 ()      Ports: 445/open/tcp//microsoft-ds///, 139/closed/tcp//netbios-ssn///
+DEBUG: # Nmap done at DATE -- 1 IP address (1 host up) scanned in 0.04 seconds
+EOF
+
+simple_test
diff --git a/tests/eventscripts/simple/50.samba.monitor.110.sh b/tests/eventscripts/simple/50.samba.monitor.110.sh
new file mode 100755 (executable)
index 0000000..80264cc
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "port 139 down, ctdb checktcpport/nmap not implemented, debug"
+
+ctdb_not_implemented "checktcpport"
+export FAKE_NMAP_NOT_FOUND="yes"
+
+# This has to go before the setup, otherwise it will write a dud file.
+export CTDB_DEBUGLEVEL=4
+
+setup_nmap_output_filter
+
+setup_samba
+tcp_port_down 139
+
+required_result 1 <<EOF
+DEBUG: ctdb_check_ports - checker ctdb not implemented
+DEBUG: output from checker was:
+DEBUG: ctdb checktcpport 445 (exited with 1) with output:
+$ctdb_not_implemented
+DEBUG: ctdb_check_ports - checker nmap not implemented
+DEBUG: output from checker was:
+DEBUG: sh: nmap: command not found
+ERROR: samba tcp port 139 is not responding
+DEBUG: netstat -l -t -n shows this output:
+DEBUG: Active Internet connections (servers only)
+DEBUG: Proto Recv-Q Send-Q Local Address           Foreign Address         State
+DEBUG: tcp        0      0 0.0.0.0:445             0.0.0.0:*               LISTEN
+EOF
+
+simple_test
diff --git a/tests/eventscripts/simple/50.samba.monitor.111.sh b/tests/eventscripts/simple/50.samba.monitor.111.sh
new file mode 100755 (executable)
index 0000000..4651290
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "port 139 down, ctdb checktcpport/nmap/netstat not implemented"
+
+ctdb_not_implemented "checktcpport"
+export FAKE_NMAP_NOT_FOUND="yes"
+export FAKE_NETSTAT_NOT_FOUND="yes"
+
+setup_nmap_output_filter
+
+setup_samba
+tcp_port_down 139
+
+required_result 127 <<EOF
+INTERNAL ERROR: ctdb_check_ports - no working checkers in CTDB_TCP_PORT_CHECKERS="ctdb nmap netstat"
+EOF
+
+simple_test
diff --git a/tests/eventscripts/simple/60.nfs.monitor.161.sh b/tests/eventscripts/simple/60.nfs.monitor.161.sh
new file mode 100755 (executable)
index 0000000..4abe68e
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "2nd share missing"
+
+setup_nfs
+
+shares_missing "ERROR: nfs directory \"%s\" not available" 2
+
+required_result 1 "$MISSING_SHARES_TEXT"
+
+simple_test
diff --git a/tests/eventscripts/simple/60.nfs.monitor.162.sh b/tests/eventscripts/simple/60.nfs.monitor.162.sh
new file mode 100755 (executable)
index 0000000..865b1c6
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+. "${EVENTSCRIPTS_TESTS_DIR}/common.sh"
+
+define_test "2nd share missing, skipping share checks"
+
+setup_nfs
+export CTDB_NFS_SKIP_SHARE_CHECK="yes"
+
+shares_missing "ERROR: nfs directory \"%s\" not available" 2
+
+ok_null
+
+simple_test
index ce06a35cbd86c6312714ed7be04bb69c720c294a..155f518ddc9ace3058204c100bce1448f625aec1 100755 (executable)
@@ -2,6 +2,8 @@
 
 prog="ctdb"
 
+not_implemented_exit_code=1
+
 usage ()
 {
     cat >&2 <<EOF
@@ -9,26 +11,28 @@ Usage: $prog [-Y] cmd
 
 A fake CTDB stub that prints items depending on the variables
 FAKE_CTDB_PNN (default 0) depending on command-line options.
-
-Note that -Y is ignored.
-
 EOF
     exit 1
 }
 
-# $POSIXLY_CORRECT means that the command passed to onnode can take
-# options and getopt won't reorder things to make them options to this
-# script.
+not_implemented ()
+{
+    echo "${prog}: command \"$1\" not implemented in stub" >&2
+    exit $not_implemented_exit_code
+}
+
+# Don't set $POSIXLY_CORRECT here.
 _temp=$(getopt -n "$prog" -o "Yvh" -l help -- "$@") || \
     usage
 
 eval set -- "$_temp"
 
 verbose=false
+machine_readable=false
 
 while true ; do
     case "$1" in
-       -Y) shift ;;
+       -Y) machine_readable=true ; shift ;;
        -v) verbose=true ; shift ;;
        --) shift ; break ;;
        -h|--help|*) usage ;; # * shouldn't happen, so this is reasonable.
@@ -51,6 +55,13 @@ setup_pstore ()
     mkdir -p "$pstore_dir"
 }
 
+# For testing backward compatibility...
+for i in $CTDB_NOT_IMPLEMENTED ; do
+    if [ "$i" = "$1" ] ; then
+       not_implemented "$i"
+    fi
+done
+
 case "$1" in
     ip)
        # NOTE: all nodes share the same public addresses file.
@@ -159,8 +170,58 @@ case "$1" in
                exit 1
        esac
        ;;
+    getdebug)
+       case "${CTDB_DEBUGLEVEL:-0}" in
+           -3) _t="EMERG"  ;;
+           -2) _t="ALERT"  ;;
+           -1) _t="CRIT"   ;;
+           0) _t="ERR"     ;;
+           1) _t="WARNING" ;;
+           2) _t="NOTICE"  ;;
+           3) _t="INFO"    ;;
+           4) _t="DEBUG"   ;;
+           *) _t="ERR"     ;;
+       esac
+
+       cat<<EOF
+:Name:Level:
+:${_t}:${CTDB_DEBUGLEVEL}:
+EOF
+       ;;
+    checktcpport)
+       for _i in $FAKE_TCP_LISTEN ; do
+           if [ "$2" = "${_i##*:}" ] ; then
+               exit 98
+           fi
+       done
+
+       exit 0
+       ;;
+    scriptstatus)
+       $machine_readable || not_implemented "$1, without -Y"
+       [ "$2" != "all" ] || not_implemented "scriptstatus all"
+       # For now just assume everything is good.
+       echo ":Type:Name:Code:Status:Start:End:Error Output...:"
+       for _i in "$CTDB_BASE/events.d/"*.* ; do
+           _d1=$(date '+%s.%N')
+           _b="${_i##*/}" # basename
+
+           _f="$FAKE_CTDB_SCRIPTSTATUS/$_b"
+           if [ -r "$_f" ] ; then
+               read _code _status _err_out <"$_f"
+           else
+               _code="0"
+               _status="OK"
+               if [ ! -x "$_i" ] ; then
+                   _status="DISABLED"
+                   _code="-8"
+               fi
+               _err_out=""
+           fi
+           _d2=$(date '+%s.%N')
+           echo ":${2:-monitor}:${_b}:${_code}:${_status}:${_d1}:${_d2}:${_err_out}:"
+       done
+       ;;
     *)
-       echo "${prog}: command \"$1\" not implemented in stub"
-       exit 1
+       not_implemented "$1"
 esac
-
index 72a11d5741390045bde3a847da5527152e3eb873..46c65225be17f5012137037fb992a033e17e6241 100755 (executable)
@@ -3,5 +3,11 @@
 opts="10.0.0.0/16(rw,async,insecure,no_root_squash,no_subtree_check)"
 
 for i in $FAKE_SHARES ; do
-    echo "${i} ${opts}"
+    # Directories longer than 15 characters are printed on their own
+    # line.
+    if [ ${#i} -ge 15 ] ; then
+       printf '%s\n\t\t%s\n' "$i" "$opts"
+    else
+       printf '%s\t%s\n' "$i" "$opts"
+    fi
 done
index cf0656651aa499089f9c08bfa30f440556becf67..f46c9365193622152efcb27130bae7db9ea5b6b0 100755 (executable)
@@ -2,13 +2,20 @@
 
 prog="netstat"
 
+# Pretty that we're the shell and that this command could not be
+# found.
+if [ "$FAKE_NETSTAT_NOT_FOUND" = "yes" ] ; then
+    echo "sh: ${prog}: command not found" >&2
+    exit 127
+fi
+
 usage ()
 {
     cat >&2 <<EOF
 Usage: $prog [ -t | --unix ] [ -n ] [ -a ] [ -l ]
 
 A fake netstat stub that prints items depending on the variables
-FAKE_NETSTAT_TCP_ESTABLISHED, FAKE_NETSTAT_TCP_LISTEN,
+FAKE_NETSTAT_TCP_ESTABLISHED, FAKE_TCP_LISTEN,
 FAKE_NETSTAT_UNIX_LISTEN, depending on command-line options.
 
 Note that -n is ignored.
@@ -73,7 +80,7 @@ if $tcp ; then
     done
 
     if $all || $listen ; then
-       for i in $FAKE_NETSTAT_TCP_LISTEN ; do
+       for i in $FAKE_TCP_LISTEN ; do
            printf "$tcp_fmt" $i "0.0.0.0:*" "LISTEN"
        done
     fi
diff --git a/tests/eventscripts/stubs/nmap b/tests/eventscripts/stubs/nmap
new file mode 100755 (executable)
index 0000000..f01fe32
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+prog="nmap"
+
+# Pretty that we're the shell and that this command could not be
+# found.
+if [ "$FAKE_NMAP_NOT_FOUND" = "yes" ] ; then
+    echo "sh: ${prog}: command not found" >&2
+    exit 127
+fi
+
+usage ()
+{
+    cat >&2 <<EOF
+Usage: $prog -n -oG - -PS 127.0.0.1 -p <port>[,<port> ...]
+
+A fake nmap stub that prints items depending on the variable
+FAKE_TCP_LISTEN and the ports specified.
+
+Note that all options apart from -p are ignored.
+
+EOF
+    exit 1
+}
+
+ports=""
+
+parse_options ()
+{
+    _temp=$(getopt -n "$prog" -a -o "np:" -l help -l PS: -l oG: -- "$@")
+
+    [ $? != 0 ] && usage
+
+    eval set -- "$_temp"
+
+    while true ; do
+       case "$1" in
+           -n) shift ;;
+           --oG|--PS) shift 2 ;;
+           -p) ports="${ports}${ports:+ }${2//,/ }" ; shift 2 ;;
+           --) shift ; break ;;
+           -h|--help|*) usage ;; # * shouldn't happen, so this is reasonable.
+       esac
+    done
+
+    [ $# -gt 0 ] && usage
+
+    [ -n "$ports" ] || usage
+}
+
+# For printing out...
+args="$*"
+
+parse_options "$@"
+
+port_states=""
+
+for p in $ports ; do
+    pn=$(getent services "$p" | sed -e 's@[[:space:]].*@@')
+    for i in $FAKE_TCP_LISTEN ; do
+       lp="${i##*:}"
+       if [ "$p" = "$lp" ] ; then
+           port_states="${port_states}${port_states:+, }${p}/open/tcp//${pn}///"
+           continue 2
+       fi
+    done
+    port_states="${port_states}${port_states:+, }${p}/closed/tcp//${pn}///"
+done
+
+cat <<EOF
+# Nmap 5.21 scan initiated $(date) as: nmap $args
+Host: 127.0.0.1 ()     Status: Up
+Host: 127.0.0.1 ()     Ports: $port_states
+# Nmap done at $(date) -- 1 IP address (1 host up) scanned in 0.04 seconds
+EOF
diff --git a/tests/eventscripts/stubs/sleep b/tests/eventscripts/stubs/sleep
new file mode 100755 (executable)
index 0000000..e454244
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+if [ "$FAKE_SLEEP_REALLY" = "yes" ] ; then
+    /bin/sleep "$@"
+elif [ -n "$FAKE_SLEEP_FORCE" ] ; then
+    /bin/sleep "$FAKE_SLEEP_FORCE"
+else
+    :
+fi
index 6d812237ec2605692be902ae96f36f5f50434ef5..8e0e0d79561649a70ff956f9c7c47e61a72e0bf7 100644 (file)
@@ -222,10 +222,11 @@ int main(int argc, const char *argv[])
        ev = event_context_init(NULL);
 
        /* initialise ctdb */
-       ctdb = ctdb_cmdline_client(ev);
+       ctdb = ctdb_cmdline_client(ev, timeval_current_ofs(3, 0));
 
        /* attach to a specific database */
-       ctdb_db = ctdb_attach(ctdb, "test.tdb", false, 0);
+       ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0), "test.tdb",
+                             false, 0);
        if (!ctdb_db) {
                printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
                exit(1);
index a8f38eeea3e9eb5a4dfe2df9e0221558272fa778..f0311c8948e0e590046e13a9b6bac0c2eb2c11ff 100644 (file)
@@ -207,7 +207,7 @@ int main(int argc, const char *argv[])
 
        ev = event_context_init(NULL);
 
-       ctdb = ctdb_cmdline_client(ev);
+       ctdb = ctdb_cmdline_client(ev, timeval_current_ofs(3, 0));
 
        if (ctdb == NULL) {
                printf("failed to connect to ctdb daemon.\n");
@@ -218,7 +218,8 @@ int main(int argc, const char *argv[])
                                 &cluster_ready);
 
        /* attach to a specific database */
-       ctdb_db = ctdb_attach(ctdb, "test.tdb", false, 0);
+       ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0), "test.tdb",
+                             false, 0);
        if (!ctdb_db) {
                printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
                exit(1);
index 7348aa698696408f293caa8d6fa89a448d4992bc..042e605f9684e41dc07f691606d432d636fb3b08 100644 (file)
@@ -114,7 +114,7 @@ int main(int argc, const char *argv[])
 
        ev = event_context_init(NULL);
 
-       ctdb = ctdb_cmdline_client(ev);
+       ctdb = ctdb_cmdline_client(ev, timeval_current_ofs(3, 0));
 
        if (ctdb == NULL) {
                printf("failed to connect to ctdb deamon.\n");
@@ -122,7 +122,8 @@ int main(int argc, const char *argv[])
        }
 
        /* attach to a specific database */
-       ctdb_db = ctdb_attach(ctdb, "test.tdb", false, 0);
+       ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0), "test.tdb",
+                             false, 0);
        if (!ctdb_db) {
                printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
                exit(1);
index 6074b6a1e361e5a3f86dace78eb374b52aeb3fc0..98c100f4ed14104dfacb2c137eee8498db913685 100644 (file)
@@ -220,7 +220,7 @@ int main(int argc, const char *argv[])
 
        ev = event_context_init(NULL);
 
-       ctdb = ctdb_cmdline_client(ev);
+       ctdb = ctdb_cmdline_client(ev, timeval_current_ofs(3, 0));
        if (ctdb == NULL) {
                printf("Could not attach to daemon\n");
                return 1;
@@ -228,9 +228,11 @@ int main(int argc, const char *argv[])
 
        /* attach to a specific database */
        if (unsafe_writes == 1) {
-               ctdb_db = ctdb_attach(ctdb, "persistent.tdb", true, TDB_NOSYNC);
+               ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0),
+                                     "persistent.tdb", true, TDB_NOSYNC);
        } else {
-               ctdb_db = ctdb_attach(ctdb, "persistent.tdb", true, 0);
+               ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0),
+                                     "persistent.tdb", true, 0);
        }
 
        if (!ctdb_db) {
index 5b7eef83d29b5f91380bbd240fc942838a9be798..dbaa4ba0fc864fa31ed2f34500aefdf03d65bd2d 100644 (file)
@@ -181,7 +181,7 @@ int main(int argc, const char *argv[])
 
        ev = event_context_init(NULL);
 
-       ctdb = ctdb_cmdline_client(ev);
+       ctdb = ctdb_cmdline_client(ev, timeval_current_ofs(3, 0));
 
        if (ctdb == NULL) {
                printf("failed to connect to daemon\n");
@@ -189,7 +189,8 @@ int main(int argc, const char *argv[])
        }
 
        /* attach to a specific database */
-       ctdb_db = ctdb_attach(ctdb, "test.tdb", false, 0);
+       ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0), "test.tdb",
+                             false, 0);
        if (!ctdb_db) {
                printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
                exit(1);
index 82669b7b6dd784d8037cfa9d26d4922576ccf0f0..c657f7850e4dbe7abac9a763d5c18fe3836353eb 100644 (file)
@@ -136,7 +136,7 @@ int main(int argc, const char *argv[])
 
        ev = event_context_init(NULL);
 
-       ctdb = ctdb_cmdline_client(ev);
+       ctdb = ctdb_cmdline_client(ev, timeval_current_ofs(3, 0));
 
        if (ctdb == NULL) {
                printf("failed to connect to ctdb daemon.\n");
@@ -144,7 +144,7 @@ int main(int argc, const char *argv[])
        }
 
        /* attach to a specific database */
-       ctdb_db = ctdb_attach(ctdb, "test.tdb", false, 0);
+       ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0), "test.tdb", false, 0);
        if (!ctdb_db) {
                printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
                exit(1);
index 39e40dd16bb7f552c15ecd7d97754a2de70cea8f..65088747176ce5b05fab93206e70c319884bea07 100644 (file)
@@ -252,7 +252,7 @@ int main(int argc, const char *argv[])
 
        ev = event_context_init(NULL);
 
-       ctdb = ctdb_cmdline_client(ev);
+       ctdb = ctdb_cmdline_client(ev, timeval_current_ofs(3, 0));
        if (ctdb == NULL) {
                DEBUG(DEBUG_ERR, ("Could not attach to daemon\n"));
                return 1;
@@ -260,9 +260,11 @@ int main(int argc, const char *argv[])
 
        /* attach to a specific database */
        if (unsafe_writes == 1) {
-               ctdb_db = ctdb_attach(ctdb, "transaction.tdb", true, TDB_NOSYNC);
+               ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0),
+                                     "transaction.tdb", true, TDB_NOSYNC);
        } else {
-               ctdb_db = ctdb_attach(ctdb, "transaction.tdb", true, 0);
+               ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0),
+                                     "transaction.tdb", true, 0);
        }
 
        if (!ctdb_db) {
index c4510e43c93a41cb44238c9cd5446a77b930ae15..218e7f777ded426e531365a8b9e2123633f44fb8 100644 (file)
@@ -89,10 +89,10 @@ int main(int argc, const char *argv[])
 
        ev = event_context_init(NULL);
 
-       ctdb = ctdb_cmdline_client(ev);
+       ctdb = ctdb_cmdline_client(ev, timeval_current_ofs(3, 0));
 
        /* attach to a specific database */
-       ctdb_db = ctdb_attach(ctdb, dbname, false, 0);
+       ctdb_db = ctdb_attach(ctdb, timeval_current_ofs(2, 0), dbname, false, 0);
        if (!ctdb_db) {
                printf("ctdb_attach failed - %s\n", ctdb_errstr(ctdb));
                exit(1);
index 331392c2e430a9011850efb66d0bae07e2806403..26c0bfb76980a84bf75f1a456d8e421d322f4373 100644 (file)
@@ -624,9 +624,10 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
                return ret;
        }
 
-       if(options.machinereadable){
-               printf(":Node:IP:Disconnected:Banned:Disabled:Unhealthy:Stopped:Inactive:PartiallyOnline:\n");
-               for(i=0;i<nodemap->num;i++){
+       if (options.machinereadable) {
+               printf(":Node:IP:Disconnected:Banned:Disabled:Unhealthy:Stopped"
+                      ":Inactive:PartiallyOnline:ThisNode:\n");
+               for (i=0;i<nodemap->num;i++) {
                        int partially_online = 0;
                        int j;
 
@@ -650,7 +651,7 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
                                        talloc_free(ifaces);
                                }
                        }
-                       printf(":%d:%s:%d:%d:%d:%d:%d:%d:%d:\n", nodemap->nodes[i].pnn,
+                       printf(":%d:%s:%d:%d:%d:%d:%d:%d:%d:%d:\n", nodemap->nodes[i].pnn,
                                ctdb_addr_to_str(&nodemap->nodes[i].addr),
                               !!(nodemap->nodes[i].flags&NODE_FLAGS_DISCONNECTED),
                               !!(nodemap->nodes[i].flags&NODE_FLAGS_BANNED),
@@ -658,7 +659,8 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
                               !!(nodemap->nodes[i].flags&NODE_FLAGS_UNHEALTHY),
                               !!(nodemap->nodes[i].flags&NODE_FLAGS_STOPPED),
                               !!(nodemap->nodes[i].flags&NODE_FLAGS_INACTIVE),
-                              partially_online);
+                              partially_online,
+                              (nodemap->nodes[i].pnn == mypnn));
                }
                return 0;
        }
@@ -737,17 +739,15 @@ static int control_status(struct ctdb_context *ctdb, int argc, const char **argv
                printf("hash:%d lmaster:%d\n", i, vnnmap->map[i]);
        }
 
-       ret = ctdb_ctrl_getrecmode(ctdb, ctdb, TIMELIMIT(), options.pnn, &recmode);
-       if (ret != 0) {
+       if (!ctdb_getrecmode(ctdb_connection, options.pnn, &recmode)) {
                DEBUG(DEBUG_ERR, ("Unable to get recmode from node %u\n", options.pnn));
-               return ret;
+               return -1;
        }
        printf("Recovery mode:%s (%d)\n",recmode==CTDB_RECOVERY_NORMAL?"NORMAL":"RECOVERY",recmode);
 
-       ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), options.pnn, &recmaster);
-       if (ret != 0) {
+       if (!ctdb_getrecmaster(ctdb_connection, options.pnn, &recmaster)) {
                DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn));
-               return ret;
+               return -1;
        }
        printf("Recovery master:%d\n",recmaster);
 
@@ -961,7 +961,7 @@ static int control_one_scriptstatus(struct ctdb_context *ctdb,
                        break;
                }
                if (options.machinereadable) {
-                       printf("%s:%s:%i:%s:%lu.%06lu:%lu.%06lu:%s:\n",
+                       printf(":%s:%s:%i:%s:%lu.%06lu:%lu.%06lu:%s:\n",
                               ctdb_eventscript_call_names[type],
                               script_status->scripts[i].name,
                               script_status->scripts[i].status,
@@ -1095,13 +1095,11 @@ static int control_disablescript(struct ctdb_context *ctdb, int argc, const char
  */
 static int control_recmaster(struct ctdb_context *ctdb, int argc, const char **argv)
 {
-       int ret;
        uint32_t recmaster;
 
-       ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), options.pnn, &recmaster);
-       if (ret != 0) {
+       if (!ctdb_getrecmaster(ctdb_connection, options.pnn, &recmaster)) {
                DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn));
-               return ret;
+               return -1;
        }
        printf("%d\n",recmaster);
 
@@ -1596,10 +1594,9 @@ again:
        }
 
 
-       ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), options.pnn, &recmaster);
-       if (ret != 0) {
+       if (!ctdb_getrecmaster(ctdb_connection, options.pnn, &recmaster)) {
                DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn));
-               return ret;
+               return -1;
        }
 
        /* verify the node exists */
@@ -2543,15 +2540,13 @@ static uint32_t get_generation(struct ctdb_context *ctdb)
                }
 
                /* get the recmaster */
-               ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), CTDB_CURRENT_NODE, &recmaster);
-               if (ret != 0) {
+               if (!ctdb_getrecmaster(ctdb_connection, CTDB_CURRENT_NODE, &recmaster)) {
                        DEBUG(DEBUG_ERR, ("Unable to get recmaster from node %u\n", options.pnn));
                        exit(10);
                }
 
                /* get recovery mode */
-               ret = ctdb_ctrl_getrecmode(ctdb, ctdb, TIMELIMIT(), recmaster, &recmode);
-               if (ret != 0) {
+               if (!ctdb_getrecmode(ctdb_connection, recmaster, &recmode)) {
                        DEBUG(DEBUG_ERR, ("Unable to get recmode from node %u\n", options.pnn));
                        exit(10);
                }
@@ -2990,7 +2985,7 @@ static int control_catdb(struct ctdb_context *ctdb, int argc, const char **argv)
                return -1;
        }
 
-       ctdb_db = ctdb_attach(ctdb, db_name, persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0);
 
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
@@ -3048,7 +3043,7 @@ static int control_cattdb(struct ctdb_context *ctdb, int argc, const char **argv
                return -1;
        }
 
-       ctdb_db = ctdb_attach(ctdb, db_name, false, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, false, 0);
 
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
@@ -3092,7 +3087,7 @@ static int control_readkey(struct ctdb_context *ctdb, int argc, const char **arg
                return -1;
        }
 
-       ctdb_db = ctdb_attach(ctdb, db_name, persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0);
 
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
@@ -3141,7 +3136,7 @@ static int control_writekey(struct ctdb_context *ctdb, int argc, const char **ar
                return -1;
        }
 
-       ctdb_db = ctdb_attach(ctdb, db_name, persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0);
 
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
@@ -3205,7 +3200,7 @@ static int control_pfetch(struct ctdb_context *ctdb, int argc, const char **argv
                return -1;
        }
 
-       ctdb_db = ctdb_attach(ctdb, db_name, persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0);
 
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
@@ -3368,8 +3363,7 @@ static int control_pstore(struct ctdb_context *ctdb, int argc, const char **argv
 
        db_name = argv[0];
 
-       ctdb_db = ctdb_attach(ctdb, db_name, true, 0);
-
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, true, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
                talloc_free(tmp_ctx);
@@ -3657,7 +3651,6 @@ static int control_getdbstatus(struct ctdb_context *ctdb, int argc, const char *
 static int control_isnotrecmaster(struct ctdb_context *ctdb, int argc, const char **argv)
 {
        uint32_t mypnn, recmaster;
-       int ret;
 
        mypnn = ctdb_ctrl_getpnn(ctdb, TIMELIMIT(), options.pnn);
        if (mypnn == -1) {
@@ -3665,8 +3658,7 @@ static int control_isnotrecmaster(struct ctdb_context *ctdb, int argc, const cha
                return 1;
        }
 
-       ret = ctdb_ctrl_getrecmaster(ctdb, ctdb, TIMELIMIT(), options.pnn, &recmaster);
-       if (ret != 0) {
+       if (!ctdb_getrecmaster(ctdb_connection, options.pnn, &recmaster)) {
                printf("Failed to get the recmaster\n");
                return 1;
        }
@@ -4023,7 +4015,7 @@ static int control_attach(struct ctdb_context *ctdb, int argc, const char **argv
                persistent = true;
        }
 
-       ctdb_db = ctdb_attach(ctdb, db_name, persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), db_name, persistent, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", db_name));
                return -1;
@@ -4241,7 +4233,7 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
                        DEBUG(DEBUG_ERR,("database '%s' is unhealthy: %s\n",
                                         argv[0], reason));
 
-                       DEBUG(DEBUG_ERR,("disallow backup : tunnable AllowUnhealthyDBRead = %u\n",
+                       DEBUG(DEBUG_ERR,("disallow backup : tunable AllowUnhealthyDBRead = %u\n",
                                         allow_unhealthy));
                        talloc_free(tmp_ctx);
                        return -1;
@@ -4254,7 +4246,7 @@ static int control_backupdb(struct ctdb_context *ctdb, int argc, const char **ar
                                     allow_unhealthy));
        }
 
-       ctdb_db = ctdb_attach(ctdb, argv[0], dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), argv[0], dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", argv[0]));
                talloc_free(tmp_ctx);
@@ -4401,7 +4393,7 @@ static int control_restoredb(struct ctdb_context *ctdb, int argc, const char **a
                dbname, tbuf);
 
 
-       ctdb_db = ctdb_attach(ctdb, dbname, dbhdr.persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), dbname, dbhdr.persistent, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,("Unable to attach to database '%s'\n", dbname));
                talloc_free(tmp_ctx);
@@ -4653,7 +4645,7 @@ static int control_wipedb(struct ctdb_context *ctdb, int argc,
                return -1;
        }
 
-       ctdb_db = ctdb_attach(ctdb, argv[0], dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), argv[0], dbmap->dbs[i].flags & CTDB_DB_FLAGS_PERSISTENT, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR, ("Unable to attach to database '%s'\n",
                                  argv[0]));
@@ -5232,7 +5224,7 @@ int main(int argc, const char *argv[])
                        }
 
                        /* initialise ctdb */
-                       ctdb = ctdb_cmdline_client(ev);
+                       ctdb = ctdb_cmdline_client(ev, TIMELIMIT());
 
                        if (ctdb_commands[i].without_daemon == false) {
                                const char *socket_name;
index cbe3c5c8bbcb5f6b52e91a034b39774fbf0370f4..a9dc28a9a60edcab61c634aadcf50df9f6f2354a 100644 (file)
@@ -260,7 +260,7 @@ static int ctdb_vacuum_db(struct ctdb_context *ctdb, uint32_t db_id, struct ctdb
                return -1;
        }
 
-       ctdb_db = ctdb_attach(ctdb, name, persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), name, persistent, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,(__location__ " Failed to attach to database '%s'\n", name));
                talloc_free(vdata);
@@ -581,7 +581,7 @@ static int ctdb_repack_db(struct ctdb_context *ctdb, uint32_t db_id,
                return -1;
        }
 
-       ctdb_db = ctdb_attach(ctdb, name, persistent, 0);
+       ctdb_db = ctdb_attach(ctdb, TIMELIMIT(), name, persistent, 0);
        if (ctdb_db == NULL) {
                DEBUG(DEBUG_ERR,(__location__ " Failed to attach to database '%s'\n", name));
                return -1;
diff --git a/utils/pmda/Install b/utils/pmda/Install
new file mode 100644 (file)
index 0000000..a56a635
--- /dev/null
@@ -0,0 +1,36 @@
+#! /bin/sh
+#
+# Copyright (c) 1997 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Install the ctdb PMDA and/or PMNS
+#
+
+. $PCP_DIR/etc/pcp.env
+. $PCP_SHARE_DIR/lib/pmdaproc.sh
+
+iam=ctdb
+pmda_interface=2
+
+# runs as daemon and only supports pipe IPC
+daemon_opt=true
+dso_opt=false
+pipe_opt=true
+socket_opt=false
+
+pmdaSetup
+pmdaInstall
+exit 0
diff --git a/utils/pmda/README b/utils/pmda/README
new file mode 100644 (file)
index 0000000..f8dbbbc
--- /dev/null
@@ -0,0 +1,84 @@
+CTDB PMDA
+===========
+
+This PMDA extracts metrics from the locally running ctdbd daemon for
+export to PMCD.
+
+Note:
+       This PMDA may be remade from source and hence requires IDO (or
+       more specifically a C compiler) to be installed.
+
+       Uses of make(1) may fail (without removing or clobbering files)
+       if the C compiler cannot be found.  This is most likely to
+       happen when running the PMDA ./Install script.
+
+       The only remedial action is to install the C compiler, or
+       hand-craft changes to the Makefile.
+
+Metrics
+=======
+
+The file ./help contains descriptions for all of the metrics exported
+by this PMDA.
+
+Once the PMDA has been installed, the following command will list all
+the available metrics and their explanatory "help" text:
+
+       $ pminfo -fT ctdb
+
+Installation
+============
+
+ +  # cd $PCP_PMDAS_DIR/ctdb
+
+ +  Check that there is no clash in the Performance Metrics Domain
+    defined in ./domain.h and the other PMDAs currently in use (see
+    $PCP_PMCDCONF_PATH).  If there is, edit ./domain.h to choose another
+    domain number.
+
+ +  Then simply use
+
+       # ./Install
+
+    and choose both the "collector" and "monitor" installation
+    configuration options.
+
+    You will be prompted to choose either a daemon implementation
+    or a DSO implementation of the PMDA, and in the case of the daemon
+    variant to select an IPC method -- everything else is automated
+
+De-installation
+===============
+
+ +  Simply use
+
+       # cd $PCP_PMDAS_DIR/ctdb
+       # ./Remove
+
+Troubleshooting
+===============
+
+ +  After installing or restarting the agent, the PMCD log file
+    ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file
+    ($PCP_LOG_DIR/pmcd/pmda_ctdb.log) should be checked for any warnings
+    or errors.
+
+
+Adding a New Metric
+===================
+
+This section walks through the development task of adding a new metric to the
+CTDB PMDA.
+
+  + Define the metric in the pmns file with a unique metric id. See the pmns(4)
+    man page for details.
+
+  + Add a description of the metric to the help file.
+
+  + Taking note of the previously assigned metric id, add a new entry to the
+    metrictab structure in pmda_ctdb.c. See the pmdaInit(3) man page for
+    details.
+
+  + Ensure the counter is already a member of the ctdb_statistics structure.
+    Finally, add code to pmda_ctdb_fetch_cb() to handle fetch requests for the
+    newly defined metric.
diff --git a/utils/pmda/Remove b/utils/pmda/Remove
new file mode 100644 (file)
index 0000000..7d1c509
--- /dev/null
@@ -0,0 +1,29 @@
+#! /bin/sh
+#
+# Copyright (c) 1997 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# Remove the ctdb PMDA
+#
+
+. $PCP_DIR/etc/pcp.env
+. $PCP_SHARE_DIR/lib/pmdaproc.sh
+
+iam=ctdb
+
+pmdaSetup
+pmdaRemove
+exit 0
diff --git a/utils/pmda/domain.h b/utils/pmda/domain.h
new file mode 100644 (file)
index 0000000..d9b316f
--- /dev/null
@@ -0,0 +1,19 @@
+/* domain.h
+ *
+ * Copyright (c) 2004-2009 Silicon Graphics, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#define CTDB 155
diff --git a/utils/pmda/help b/utils/pmda/help
new file mode 100644 (file)
index 0000000..0e9984e
--- /dev/null
@@ -0,0 +1,106 @@
+#
+# Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# ctdb PMDA help file in the ASCII format
+#
+# lines beginning with a # are ignored
+# lines beginning @ introduce a new entry of the form
+#  @ metric_name oneline-text
+#  help test goes
+#  here over multiple lines
+#  ...
+#
+# the metric_name is decoded against the default PMNS -- as a special case,
+# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an
+# instance domain identification, and the text describes the instance domain
+#
+# blank lines before the @ line are ignored
+#
+
+@ ctdb.num_clients number of clients connected to ctdbd
+
+@ ctdb.frozen whether any databases are frozen
+
+@ ctdb.recovering whether recovery is active
+
+@ ctdb.client_packets_sent number of packets sent to all clients
+
+@ ctdb.client_packets_recv number of packets received from all clients
+
+@ ctdb.node_packets_sent number of packets sent to other nodes
+
+@ ctdb.node_packets_recv number of packets received from other nodes
+
+@ ctdb.keepalive_packets_sent number of keepalive packets sent to other nodes
+
+@ ctdb.keepalive_packets_recv number of keepalive packets received from other nodes
+
+@ ctdb.node.req_call number of node CTDB_REQ_CALL packets handled
+
+@ ctdb.node.reply_call number of node CTDB_REPLY_CALL packets handled
+
+@ ctdb.node.req_dmaster number of node CTDB_REQ_DMASTER packets handled
+
+@ ctdb.node.reply_dmaster number of node CTDB_REPLY_DMASTER packets handled
+
+@ ctdb.node.reply_error number of node CTDB_REPLY_ERROR packets handled
+
+@ ctdb.node.req_message number of node CTDB_REQ_MESSAGE packets handled
+
+@ ctdb.node.req_control number of node CTDB_REQ_CONTROL packets handled
+
+@ ctdb.node.reply_control number of node CTDB_REPLY_CONTROL packets handled
+
+@ ctdb.client.req_call number of client CTDB_REQ_CALL packets handled
+
+@ ctdb.client.req_message number of client CTDB_REQ_MESSAGE packets handled
+
+@ ctdb.client.req_control number of client CTDB_REQ_CONTROL packets handled
+
+@ ctdb.timeouts.call (counter not implemented) number of call timeouts
+
+@ ctdb.timeouts.control number of node control message request timeouts awaiting reply
+
+@ ctdb.timeouts.traverse number of database traversal timeouts
+
+@ ctdb.total_calls total number of client ctdb request calls received
+
+@ ctdb.pending_calls total number of client ctdb request calls in progress
+
+@ ctdb.lockwait_calls number of tdb chainlock lockwait calls
+
+@ ctdb.pending_lockwait_calls number of lockwait calls waiting for a lock
+
+@ ctdb.childwrite_calls number of childwrite calls
+
+@ ctdb.pending_childwrite_calls number of childwrite calls in progress
+
+@ ctdb.memory_used total size of the ctdbd null talloc pool
+
+@ ctdb.max_hop_count maximum hops performed by a CTDB_REQ_CALL packet
+
+@ ctdb.max_reclock_ctdbd maximum recovery lock latency during setrecmode
+
+@ ctdb.max_reclock_recd maximum recovery lock latency as reported by the recovery process
+
+@ ctdb.max_call_latency maximum time spent handling a client request call
+
+@ ctdb.max_lockwait_latency maximum time spent waiting for a tdb chainlock
+
+@ ctdb.max_childwrite_latency maximum time spent performing a childwrite
+
+@ ctdb.num_recoveries number of recoveries finished
diff --git a/utils/pmda/pmda_ctdb.c b/utils/pmda/pmda_ctdb.c
new file mode 100644 (file)
index 0000000..927fea5
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * CTDB Performance Metrics Domain Agent (PMDA) for Performance Co-Pilot (PCP)
+ *
+ * Copyright (c) 1995,2004 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2011 David Disseldorp
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <pcp/pmapi.h>
+#include <pcp/impl.h>
+#include <pcp/pmda.h>
+#include "../../include/includes.h"
+#include "../../lib/tevent/tevent.h"
+#include "../../include/ctdb.h"
+#include "../../include/ctdb_private.h"
+#include "../../include/ctdb_protocol.h"
+#include "domain.h"
+
+/*
+ * CTDB PMDA
+ *
+ * This PMDA connects to the locally running ctdbd daemon and pulls
+ * statistics for export via PCP. The ctdbd Unix domain socket path can be
+ * specified with the CTDB_SOCKET environment variable, otherwise the default
+ * path is used.
+ */
+
+/*
+ * All metrics supported in this PMDA - one table entry for each.
+ * The 4th field specifies the serial number of the instance domain
+ * for the metric, and must be either PM_INDOM_NULL (denoting a
+ * metric that only ever has a single value), or the serial number
+ * of one of the instance domains declared in the instance domain table
+ * (i.e. in indomtab, above).
+ */
+static pmdaMetric metrictab[] = {
+       /* num_clients */
+       { NULL, { PMDA_PMID(0,0), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,0,0,0,0,0) }, },
+       /* frozen */
+       { NULL, { PMDA_PMID(1,2), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,0,0,0,0,0) }, },
+       /* recovering */
+       { NULL, { PMDA_PMID(3,3), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,0,0,0,0,0) }, },
+       /* client_packets_sent */
+       { NULL, { PMDA_PMID(4,4), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* client_packets_recv */
+       { NULL, { PMDA_PMID(5,5), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* node_packets_sent */
+       { NULL, { PMDA_PMID(6,6), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* node_packets_recv */
+       { NULL, { PMDA_PMID(7,7), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* keepalive_packets_sent */
+       { NULL, { PMDA_PMID(8,8), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* keepalive_packets_recv */
+       { NULL, { PMDA_PMID(9,9), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* req_call */
+       { NULL, { PMDA_PMID(10,10), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* reply_call */
+       { NULL, { PMDA_PMID(10,11), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* req_dmaster */
+       { NULL, { PMDA_PMID(10,12), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* reply_dmaster */
+       { NULL, { PMDA_PMID(10,13), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* reply_error */
+       { NULL, { PMDA_PMID(10,14), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* req_message */
+       { NULL, { PMDA_PMID(10,15), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* req_control */
+       { NULL, { PMDA_PMID(10,16), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* reply_control */
+       { NULL, { PMDA_PMID(10,17), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* req_call */
+       { NULL, { PMDA_PMID(11,18), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* req_message */
+       { NULL, { PMDA_PMID(11,19), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* req_control */
+       { NULL, { PMDA_PMID(11,20), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* call */
+       { NULL, { PMDA_PMID(12,21), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,0) }, },
+       /* control */
+       { NULL, { PMDA_PMID(12,22), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,0) }, },
+       /* traverse */
+       { NULL, { PMDA_PMID(12,23), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,0) }, },
+       /* total_calls */
+       { NULL, { PMDA_PMID(13,24), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* pending_calls */
+       { NULL, { PMDA_PMID(14,25), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,0,0,0,0,0) }, },
+       /* lockwait_calls */
+       { NULL, { PMDA_PMID(15,27), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* pending_lockwait_calls */
+       { NULL, { PMDA_PMID(16,27), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,0,0,0,0,0) }, },
+       /* childwrite_calls */
+       { NULL, { PMDA_PMID(17,28), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_COUNTER,
+               PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, },
+       /* pending_childwrite_calls */
+       { NULL, { PMDA_PMID(18,29), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,0,0,0,0,0) }, },
+       /* memory_used */
+       { NULL, { PMDA_PMID(19,30), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, },
+       /* max_hop_count */
+       { NULL, { PMDA_PMID(20,31), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,0,0,0,0,0) }, },
+       /* max_reclock_ctdbd */
+       { NULL, { PMDA_PMID(21,32), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, },
+       /* max_reclock_recd */
+       { NULL, { PMDA_PMID(22,33), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, },
+       /* max_call_latency */
+       { NULL, { PMDA_PMID(23,34), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, },
+       /* max_lockwait_latency */
+       { NULL, { PMDA_PMID(24,35), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, },
+       /* max_childwrite_latency */
+       { NULL, { PMDA_PMID(25,36), PM_TYPE_DOUBLE, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,1,0,0,PM_TIME_SEC,0) }, },
+       /* num_recoveries */
+       { NULL, { PMDA_PMID(26,37), PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_INSTANT,
+               PMDA_PMUNITS(0,0,0,0,0,0) }, },
+};
+
+static struct event_context *ev;
+static struct ctdb_context *ctdb;
+static struct ctdb_statistics *stats;
+
+static void
+pmda_ctdb_q_read_cb(uint8_t *data, size_t cnt, void *args)
+{
+       if (cnt == 0) {
+               fprintf(stderr, "ctdbd unreachable\n");
+               /* cleanup on request timeout */
+               return;
+       }
+
+       ctdb_client_read_cb(data, cnt, args);
+}
+
+
+static int
+pmda_ctdb_daemon_connect(void)
+{
+       const char *socket_name;
+       int ret;
+       struct sockaddr_un addr;
+
+       ev = event_context_init(NULL);
+       if (ev == NULL) {
+               fprintf(stderr, "Failed to init event ctx\n");
+               return -1;
+       }
+
+       ctdb = ctdb_init(ev);
+       if (ctdb == NULL) {
+               fprintf(stderr, "Failed to init ctdb\n");
+               goto err_ev;
+       }
+
+       socket_name = getenv("CTDB_SOCKET");
+       if (socket_name == NULL) {
+               socket_name = CTDB_PATH;
+       }
+
+       ret = ctdb_set_socketname(ctdb, socket_name);
+       if (ret == -1) {
+               fprintf(stderr, "ctdb_set_socketname failed - %s\n",
+                               ctdb_errstr(ctdb));
+               goto err_ctdb;
+       }
+
+       /*
+        * ctdb_socket_connect() sets a default queue callback handler that
+        * calls exit() if ctdbd is unavailable on recv, use our own wrapper to
+        * work around this
+        */
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, ctdb->daemon.name, sizeof(addr.sun_path));
+
+       ctdb->daemon.sd = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (ctdb->daemon.sd == -1) {
+               fprintf(stderr, "Failed to open client socket\n");
+               goto err_ctdb;
+       }
+
+       set_nonblocking(ctdb->daemon.sd);
+       set_close_on_exec(ctdb->daemon.sd);
+
+       if (connect(ctdb->daemon.sd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
+               fprintf(stderr, "Failed to connect to ctdb daemon via %s\n",
+                       ctdb->daemon.name);
+               goto err_sd;
+       }
+
+       ctdb->daemon.queue = ctdb_queue_setup(ctdb, ctdb, ctdb->daemon.sd,
+                                             CTDB_DS_ALIGNMENT,
+                                             pmda_ctdb_q_read_cb, ctdb,
+                                             "to-ctdbd");
+       if (ctdb->daemon.queue == NULL) {
+               fprintf(stderr, "Failed to setup queue\n");
+               goto err_sd;
+       }
+
+       ctdb->pnn = ctdb_ctrl_getpnn(ctdb, timeval_current_ofs(3, 0),
+                                    CTDB_CURRENT_NODE);
+       if (ctdb->pnn == (uint32_t)-1) {
+               fprintf(stderr, "Failed to get ctdb pnn\n");
+               goto err_sd;
+       }
+
+       return 0;
+err_sd:
+       close(ctdb->daemon.sd);
+err_ctdb:
+       talloc_free(ctdb);
+err_ev:
+       talloc_free(ev);
+       ctdb = NULL;
+       return -1;
+}
+
+static void
+pmda_ctdb_daemon_disconnect(void)
+{
+       if (ctdb->methods) {
+               ctdb->methods->shutdown(ctdb);
+       }
+
+       if (ctdb->daemon.sd != -1) {
+               close(ctdb->daemon.sd);
+       }
+
+       talloc_free(ctdb);
+       talloc_free(ev);
+       ctdb = NULL;
+}
+
+static int
+fill_node(unsigned int item, pmAtomValue *atom)
+{
+       switch (item) {
+       case 10:
+               atom->ul = stats->node.req_call;
+               break;
+       case 11:
+               atom->ul = stats->node.reply_call;
+               break;
+       case 12:
+               atom->ul = stats->node.req_dmaster;
+               break;
+       case 13:
+               atom->ul = stats->node.reply_dmaster;
+               break;
+       case 14:
+               atom->ul = stats->node.reply_error;
+               break;
+       case 15:
+               atom->ul = stats->node.req_message;
+               break;
+       case 16:
+               atom->ul = stats->node.req_control;
+               break;
+       case 17:
+               atom->ul = stats->node.reply_control;
+               break;
+       default:
+               return PM_ERR_PMID;
+       }
+
+       return 0;
+}
+
+static int
+fill_client(unsigned int item, pmAtomValue *atom)
+{
+       switch (item) {
+       case 18:
+               atom->ul = stats->client.req_call;
+               break;
+       case 19:
+               atom->ul = stats->client.req_message;
+               break;
+       case 20:
+               atom->ul = stats->client.req_control;
+               break;
+       default:
+               return PM_ERR_PMID;
+       }
+
+       return 0;
+}
+
+static int
+fill_timeout(unsigned int item, pmAtomValue *atom)
+{
+       switch (item) {
+       case 21:
+               atom->ul = stats->timeouts.call;
+               break;
+       case 22:
+               atom->ul = stats->timeouts.control;
+               break;
+       case 23:
+               atom->ul = stats->timeouts.traverse;
+               break;
+       default:
+               return PM_ERR_PMID;
+       }
+
+       return 0;
+}
+
+/*
+ * callback provided to pmdaFetch
+ */
+static int
+pmda_ctdb_fetch_cb(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
+{
+       int ret;
+       __pmID_int *id = (__pmID_int *)&(mdesc->m_desc.pmid);
+
+       if (inst != PM_IN_NULL) {
+               return PM_ERR_INST;
+       }
+
+       if (stats == NULL) {
+               fprintf(stderr, "stats not available\n");
+               ret = PM_ERR_VALUE;
+               goto err_out;
+       }
+
+
+       switch (id->cluster) {
+       case 0:
+               atom->ul = stats->num_clients;
+               break;
+       case 1:
+               atom->ul = stats->frozen;
+               break;
+       case 3:
+               atom->ul = stats->recovering;
+               break;
+       case 4:
+               atom->ul = stats->client_packets_sent;
+               break;
+       case 5:
+               atom->ul = stats->client_packets_recv;
+               break;
+       case 6:
+               atom->ul = stats->node_packets_sent;
+               break;
+       case 7:
+               atom->ul = stats->node_packets_recv;
+               break;
+       case 8:
+               atom->ul = stats->keepalive_packets_sent;
+               break;
+       case 9:
+               atom->ul = stats->keepalive_packets_recv;
+               break;
+       case 10:
+               ret = fill_node(id->item, atom);
+               if (ret) {
+                       goto err_out;
+               }
+               break;
+       case 11:
+               ret = fill_client(id->item, atom);
+               if (ret) {
+                       goto err_out;
+               }
+               break;
+       case 12:
+               ret = fill_timeout(id->item, atom);
+               if (ret) {
+                       goto err_out;
+               }
+               break;
+       case 13:
+               atom->ul = stats->total_calls;
+               break;
+       case 14:
+               atom->ul = stats->pending_calls;
+               break;
+       case 15:
+               atom->ul = stats->lockwait_calls;
+               break;
+       case 16:
+               atom->ul = stats->pending_lockwait_calls;
+               break;
+       case 17:
+               atom->ul = stats->childwrite_calls;
+               break;
+       case 18:
+               atom->ul = stats->pending_childwrite_calls;
+               break;
+       case 19:
+               atom->ul = stats->memory_used;
+               break;
+       case 20:
+               atom->ul = stats->max_hop_count;
+               break;
+       case 21:
+               atom->d = stats->reclock.ctdbd.max;
+               break;
+       case 22:
+               atom->d = stats->reclock.recd.max;
+               break;
+       case 23:
+               atom->d = stats->call_latency.max;
+               break;
+       case 24:
+               atom->d = stats->lockwait_latency.max;
+               break;
+       case 25:
+               atom->d = stats->childwrite_latency.max;
+               break;
+       case 26:
+               atom->d = stats->num_recoveries;
+               break;
+       default:
+               return PM_ERR_PMID;
+       }
+
+       ret = 0;
+err_out:
+       return ret;
+}
+
+/*
+ * This routine is called once for each pmFetch(3) operation, so is a
+ * good place to do once-per-fetch functions, such as value caching or
+ * instance domain evaluation.
+ */
+static int
+pmda_ctdb_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda)
+{
+       int ret;
+       TDB_DATA data;
+       int32_t res;
+       struct timeval ctdb_timeout;
+
+       if (ctdb == NULL) {
+               fprintf(stderr, "attempting reconnect to ctdbd\n");
+               ret = pmda_ctdb_daemon_connect();
+               if (ret < 0) {
+                       fprintf(stderr, "reconnect failed\n");
+                       return PM_ERR_VALUE;
+               }
+       }
+
+       ctdb_timeout = timeval_current_ofs(1, 0);
+       ret = ctdb_control(ctdb, ctdb->pnn, 0,
+                          CTDB_CONTROL_STATISTICS, 0, tdb_null,
+                          ctdb, &data, &res, &ctdb_timeout, NULL);
+
+       if (ret != 0 || res != 0) {
+               fprintf(stderr, "ctdb control for statistics failed, reconnecting\n");
+               pmda_ctdb_daemon_disconnect();
+               ret = PM_ERR_VALUE;
+               goto err_out;
+       }
+
+       stats = (struct ctdb_statistics *)data.dptr;
+
+       if (data.dsize != sizeof(struct ctdb_statistics)) {
+               fprintf(stderr, "incorrect statistics size %zu - not %zu\n",
+                       data.dsize, sizeof(struct ctdb_statistics));
+               ret = PM_ERR_VALUE;
+               goto err_stats;
+       }
+
+       ret = pmdaFetch(numpmid, pmidlist, resp, pmda);
+
+err_stats:
+       talloc_free(stats);
+err_out:
+       return ret;
+}
+
+/*
+ * Initialise the agent
+ */
+void
+pmda_ctdb_init(pmdaInterface *dp)
+{
+       if (dp->status != 0) {
+               return;
+       }
+
+       dp->version.two.fetch = pmda_ctdb_fetch;
+       pmdaSetFetchCallBack(dp, pmda_ctdb_fetch_cb);
+
+       pmdaInit(dp, NULL, 0, metrictab,
+                (sizeof(metrictab) / sizeof(metrictab[0])));
+}
+
+static char *
+helpfile(void)
+{
+       static char buf[MAXPATHLEN];
+
+       if (!buf[0]) {
+               snprintf(buf, sizeof(buf), "%s/ctdb/help",
+                        pmGetConfig("PCP_PMDAS_DIR"));
+       }
+       return buf;
+}
+
+static void
+usage(void)
+{
+       fprintf(stderr, "Usage: %s [options]\n\n", pmProgname);
+       fputs("Options:\n"
+         "  -d domain        use domain (numeric) for metrics domain of PMDA\n"
+         "  -l logfile       write log into logfile rather than using default log name\n"
+         "\nExactly one of the following options may appear:\n"
+         "  -i port          expect PMCD to connect on given inet port (number or name)\n"
+         "  -p               expect PMCD to supply stdin/stdout (pipe)\n"
+         "  -u socket        expect PMCD to connect on given unix domain socket\n",
+         stderr);
+       exit(1);
+}
+
+/*
+ * Set up the agent if running as a daemon.
+ */
+int
+main(int argc, char **argv)
+{
+       int err = 0;
+       char log_file[] = "pmda_ctdb.log";
+       pmdaInterface dispatch;
+
+       __pmSetProgname(argv[0]);
+
+       pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmProgname, CTDB,
+                  log_file, helpfile());
+
+       if (pmdaGetOpt(argc, argv, "d:i:l:pu:?", &dispatch, &err) != EOF) {
+               err++;
+       }
+
+       if (err) {
+               usage();
+       }
+
+       pmdaOpenLog(&dispatch);
+       pmda_ctdb_init(&dispatch);
+       pmdaConnect(&dispatch);
+       pmdaMain(&dispatch);
+
+       exit(0);
+}
+
diff --git a/utils/pmda/pmns b/utils/pmda/pmns
new file mode 100644 (file)
index 0000000..38509de
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Metrics for CTDB PMDA
+ *
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (c) 2011 David Disseldorp
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+ctdb {
+       num_clients             CTDB:0:0
+       frozen                  CTDB:1:2
+       recovering              CTDB:3:3
+       client_packets_sent     CTDB:4:4
+       client_packets_recv     CTDB:5:5
+       node_packets_sent       CTDB:6:6
+       node_packets_recv       CTDB:7:7
+       keepalive_packets_sent  CTDB:8:8
+       keepalive_packets_recv  CTDB:9:9
+       node
+       client
+       timeouts
+       total_calls             CTDB:13:24
+       pending_calls           CTDB:14:25
+       lockwait_calls          CTDB:15:27
+       pending_lockwait_calls  CTDB:16:27
+       childwrite_calls        CTDB:17:28
+       pending_childwrite_calls CTDB:18:29
+       memory_used             CTDB:19:30
+       max_hop_count           CTDB:20:31
+       max_reclock_ctdbd       CTDB:21:32
+       max_reclock_recd        CTDB:22:33
+       max_call_latency        CTDB:23:34
+       max_lockwait_latency    CTDB:24:35
+       max_childwrite_latency  CTDB:25:36
+       num_recoveries          CTDB:26:37
+}
+
+ctdb.node {
+       req_call        CTDB:10:10
+       reply_call      CTDB:10:11
+       req_dmaster     CTDB:10:12
+       reply_dmaster   CTDB:10:13
+       reply_error     CTDB:10:14
+       req_message     CTDB:10:15
+       req_control     CTDB:10:16
+       reply_control   CTDB:10:17
+}
+
+ctdb.client {
+       req_call        CTDB:11:18
+       req_message     CTDB:11:19
+       req_control     CTDB:11:20
+}
+
+ctdb.timeouts {
+       call            CTDB:12:21
+       control         CTDB:12:22
+       traverse        CTDB:12:23
+}
+
diff --git a/utils/pmda/root b/utils/pmda/root
new file mode 100644 (file)
index 0000000..ff036ed
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * fake "root" for validating the local PMNS subtree
+ */
+
+#include <stdpmid>
+
+root { ctdb }
+
+#include "pmns"
+