This is to send dbus events on major cluster events: - membership changes - application connect/dissconnet from corosync - quorum changes
dbus events can then be converted into snmp traps by foghorn or corosync-notifyd can be run to directly send snmp traps. Signed-off-by: Angus Salkeld <[email protected]> --- Makefile.am | 2 +- TODO | 53 +++- conf/COROSYNC-MIB.txt | 186 ++++++++ conf/Makefile.am | 44 ++ conf/corosync-signals.conf | 26 ++ configure.ac | 74 +++- corosync.spec.in | 23 + cts/.gitignore | 1 + init/.gitignore | 1 + init/Makefile.am | 7 +- init/notifyd.in | 147 +++++++ man/Makefile.am | 1 + man/corosync-notifyd.8 | 145 ++++++ tools/.gitignore | 1 + tools/Makefile.am | 12 +- tools/corosync-notifyd.c | 1035 ++++++++++++++++++++++++++++++++++++++++++++ 16 files changed, 1751 insertions(+), 7 deletions(-) create mode 100644 conf/COROSYNC-MIB.txt create mode 100644 conf/Makefile.am create mode 100644 conf/corosync-signals.conf create mode 100755 init/notifyd.in create mode 100644 man/corosync-notifyd.8 create mode 100644 tools/corosync-notifyd.c diff --git a/Makefile.am b/Makefile.am index f448eaa..8d8666f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -65,7 +65,7 @@ corolenstest_DATA = conf/lenses/tests/test_corosync.aug endif SUBDIRS = include lcr lib exec services tools test cts pkgconfig \ - man init + man init conf install-exec-local: $(INSTALL) -d $(DESTDIR)/${COROSYSCONFDIR}/service.d diff --git a/TODO b/TODO index 21a7a51..c78f766 100644 --- a/TODO +++ b/TODO @@ -46,13 +46,64 @@ automatically reenable a redundant ring when it has been back in service. ------------------------------------------------------------------------------ topic-snmp ------------------------------------------------------------------------------ -Main Developer: Steven Dake +Main Developer: Steven Dake & Angus Salkeld Started: Not Started Finished: 0% target: needle Description: This topic involves investigation of adding SNMP support into Corosync. +Initial requirements +-------------------- +1) send dbus signals for the following initial events: + 1.1) node joine/leave + 1.2) application connect/dissconnect from corosync + 1.3) quorum gain/lost + +2) foghorn (http://git.fedorahosted.org/git/foghorn.git) then converts + dbus signals into snmp traps + +3) if you don't want to use dbus (embedded target...) then you should be + able to use snmp-agentx. + +4) these events should be logged as well. + +Still to do: +----------- +a) add mib definitions for 1.2 + and add code to send the traps +b) chat to Yuki Sato about merging his link-status work more in. + +How to run/test (snmp only mode) +-------------------------------- +$ ./configure --enable-snmp +$ make && sudo make install +$ /etc/init.d/corosync start +$ vim /etc/snmp/snmptrapd.conf + +Add the following: +authCommunity log,execute,net public +$ /etc/init.d/snmptrapd start +$ echo "OPTIONS=\"-d\"" > /etc/sysconfig/corosync-notifyd +$ /etc/init.d/corosync-notifyd start + +I start up wireshark to see if there are any snmp traps been sent +as I am too lazy to setup a manager to receive traps. + +run a program that talks to corosync e.g. +$ corosync-objctl + +And you should get traps + +How to run/test (dbus only mode) +-------------------------------- +$ ./configure --enable-dbus +Basically the same as above except I run +$ echo "OPTIONS=\"-s\"" > /etc/sysconfig/corosync-notifyd +$ /etc/init.d/corosync-notifyd start +$ dbus-monitor --system +to see the signals getting sent + ------------------------------------------------------------------------------ topic-udpu ------------------------------------------------------------------------------ diff --git a/conf/COROSYNC-MIB.txt b/conf/COROSYNC-MIB.txt new file mode 100644 index 0000000..5143190 --- /dev/null +++ b/conf/COROSYNC-MIB.txt @@ -0,0 +1,186 @@ +COROSYNC-MIB DEFINITIONS ::= BEGIN + +-- +-- MIB objects for the corosync +-- + +IMPORTS + MODULE-IDENTITY,NOTIFICATION-TYPE, + Integer32,enterprises FROM SNMPv2-SMI + TEXTUAL-CONVENTION FROM SNMPv2-TC + SnmpAdminString FROM SNMP-FRAMEWORK-MIB + netSnmp FROM NET-SNMP-MIB + InetAddressType, InetAddress FROM INET-ADDRESS-MIB +; + +corosync MODULE-IDENTITY + LAST-UPDATED "200911061318Z" + ORGANIZATION "www.corosync.org" + CONTACT-INFO "name: Yuki Sato + email: [email protected]" + DESCRIPTION "MIB objects for the corosync" + REVISION "200911061318Z" + DESCRIPTION "First draft" + REVISION "201003251209Z" + DESCRIPTION + "Private Enterprise Number has been assigned." + ::= { enterprises 35488 } + +-- +-- top level structure +-- +corosyncNotice OBJECT IDENTIFIER ::= { corosync 1 } + +-- +-- corosync MIB entries +-- + +-- +-- Node Information +-- +corosyncNoticeNodeStatusTable OBJECT-TYPE + SYNTAX SEQUENCE OF corosyncNoticeNodeEntry + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The table contains information about the nodes in the corosync." +::= { corosyncNotice 1 } + +corosyncNoticeNodeEntry OBJECT-TYPE + SYNTAX corosyncNoticeNodeEntry + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The entry containing information about the iface." + INDEX { corosyncNoticeNodeIndex } +::= { corosyncNoticeNodeStatusTable 1 } + +corosyncNoticeNodeEntry ::= SEQUENCE { + corosyncNoticeNodeIndex Integer32, + corosyncNoticeNodeid Integer32, + corosyncNoticeNode OCTET STRING, + corosyncNoticeNodeStatus INTEGER +} + +corosyncNoticeNodeIndex OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION "The unique integer of the node." +::= { corosyncNoticeNodeEntry 1 } + +corosyncNoticeNodeid OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION "The id of the node." +::= { corosyncNoticeNodeEntry 2 } + +corosyncNoticeNode OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(1..64)) + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The iface of the node." +::= { corosyncNoticeNodeEntry 3 } + +corosyncNoticeNodeStatus OBJECT-TYPE + SYNTAX INTEGER { + unknown (0), + joined (1), + left (2) + } + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The status change of the node." +::= { corosyncNoticeNodeEntry 4 } + +-- +-- Iface(s) Information +-- +corosyncNoticeIfaceStatusTable OBJECT-TYPE + SYNTAX SEQUENCE OF corosyncNoticeIfaceEntry + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The table describes the iface(s) that are used by the corosync." +::= { corosyncNotice 2 } + +corosyncNoticeIfaceEntry OBJECT-TYPE + SYNTAX corosyncNoticeIfaceEntry + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The entry containing information about the iface." + INDEX { corosyncNoticeIfaceIndex } +::= { corosyncNoticeIfaceStatusTable 1 } + +corosyncNoticeIfaceEntry ::= SEQUENCE { + corosyncNoticeIfaceIndex INTEGER, + corosyncNoticeIface OCTET STRING, + corosyncNoticeIfaceStatus OCTET STRING +} + +corosyncNoticeIfaceIndex OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The unique integer of the iface(s)." +::= { corosyncNoticeIfaceEntry 1 } + +corosyncNoticeIface OBJECT-TYPE + SYNTAX OCTET STRING (SIZE(1..64)) + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The iface(s) of the change happened node." +::= { corosyncNoticeIfaceEntry 2 } + +corosyncNoticeIfaceStatus OBJECT-TYPE + SYNTAX INTEGER { + unknown (0), + up (1), + down (2), + faulty (3) + } + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The status change of the iface." +::= { corosyncNoticeIfaceEntry 3 } + +--corosyncNoticeIfaceStatus OBJECT-TYPE +-- SYNTAX OCTET STRING (SIZE(1..1024)) +-- MAX-ACCESS accessible-for-notify +-- STATUS current +-- DESCRIPTION +-- "The iface(s) status of the change happened node." +--::= { corosyncNoticeIfaceEntry 3 } + +-- +-- Trap Information +-- +corosyncNoticeTrap OBJECT IDENTIFIER ::= { corosync 100 } + +corosyncNoticeNodeTrap NOTIFICATION-TYPE + OBJECTS + { corosyncNoticeNodeid corosyncNoticeNode corosyncNoticeNodeStatus } + STATUS current + DESCRIPTION + "The node status change event just happened." +::= { corosyncNoticeTrap 1 } + +corosyncNoticeIfaceTrap NOTIFICATION-TYPE + OBJECTS + { corosyncNoticeNodeid corosyncNoticeIface corosyncNoticeIfaceStatus } + STATUS current + DESCRIPTION + "The iface status change event just happened." +::= { corosyncNoticeTrap 2 } + +END + + + diff --git a/conf/Makefile.am b/conf/Makefile.am new file mode 100644 index 0000000..3d7adcc --- /dev/null +++ b/conf/Makefile.am @@ -0,0 +1,44 @@ +# Copyright (c) 2009 Red Hat, Inc. +# +# Authors: Andrew Beekhof +# Steven Dake ([email protected]) +# +# This software licensed under BSD license, the text of which follows: +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# - Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# - Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# - Neither the name of the MontaVista Software, Inc. nor the names of its +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +# THE POSSIBILITY OF SUCH DAMAGE. + +EXTRA_DIST = COROSYNC-MIB.txt corosync-signals.conf + +MAINTAINERCLEANFILES = Makefile.in + +if INSTALL_MIB +mibdir = $(datadir)/snmp/mibs +mib_DATA = COROSYNC-MIB.txt +endif + +if INSTALL_DBUSCONF +dbusdir = $(sysconfdir)/dbus-1/system.d +dbus_DATA = corosync-signals.conf +endif diff --git a/conf/corosync-signals.conf b/conf/corosync-signals.conf new file mode 100644 index 0000000..4bd94d2 --- /dev/null +++ b/conf/corosync-signals.conf @@ -0,0 +1,26 @@ +<!DOCTYPE busconfig PUBLIC + "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> +<busconfig> + + <!-- Only root can own the corosync service. --> + <policy user="root"> + <allow own="com.redhat.cluster.corosync"/> + </policy> + + <policy context="default"> + <allow send_destination="com.redhat.cluster.corosync" + send_path="/com/redhat/cluster/corosync" + send_interface="com.redhat.cluster.corosync" + send_member="NodeStateChange"/> + <allow send_destination="com.redhat.cluster.corosync" + send_path="/com/redhat/cluster/corosync" + send_interface="com.redhat.cluster.corosync" + send_member="ConnectionStateChange"/> + <allow send_destination="com.redhat.cluster.corosync" + send_path="/com/redhat/cluster/corosync" + send_interface="com.redhat.cluster.corosync" + send_member="QuorumStateChange"/> + </policy> + +</busconfig> diff --git a/configure.ac b/configure.ac index 0a26adf..9beb38d 100644 --- a/configure.ac +++ b/configure.ac @@ -130,7 +130,8 @@ AC_CONFIG_FILES([Makefile cts/Makefile cts/agents/Makefile cts/CTSvars.py - tools/Makefile]) + tools/Makefile + conf/Makefile]) ### Local business @@ -241,6 +242,10 @@ AC_ARG_ENABLE([nss], [ --enable-nss : Network Security Services encryption. ],, [ enable_nss="yes" ]) +AC_ARG_ENABLE([dbus], + [ --enable-dbus : dbus events. ],, + [ enable_dbus="no" ]) + AC_ARG_ENABLE([testagents], [ --enable-testagents : Install Test Agents. ],, [ default="no" ]) @@ -281,6 +286,10 @@ AC_ARG_WITH([socket-dir], [ SOCKETDIR="$withval" ], [ SOCKETDIR="$localstatedir/run" ]) +AC_ARG_ENABLE([snmp], + [ --enable-snmp : SNMP protocol support ], + [ default="no" ]) + # OS detection # THIS SECTION MUST DIE! CP=cp @@ -385,6 +394,13 @@ if test "x${enable_nss}" = xyes; then PACKAGE_FEATURES="$PACKAGE_FEATURES nss" fi +# Look for dbus-1 +if test "x${enable_dbus}" = xyes; then + PKG_CHECK_MODULES([DBUS],[dbus-1]) + AC_DEFINE_UNQUOTED([HAVE_DBUS], 1, [have dbus]) + PACKAGE_FEATURES="$PACKAGE_FEATURES dbus" +fi + if test "x${enable_testagents}" = xyes; then AC_DEFINE_UNQUOTED([HAVE_TESTAGENTS], 1, [have testagents]) PACKAGE_FEATURES="$PACKAGE_FEATURES testagents" @@ -425,6 +441,60 @@ if test "x${enable_augeas}" = xyes; then PACKAGE_FEATURES="$PACKAGE_FEATURES augeas" fi +if test "x${enable_snmp}" = xyes; then + SNMPCONFIG="" + AC_CHECK_HEADERS(net-snmp/net-snmp-config.h) + + if test "x${ac_cv_header_net_snmp_net_snmp_config_h}" != "xyes"; then + enable_snmp=no + fi + + if test $enable_snmp != no; then + AC_PATH_PROGS(SNMPCONFIG, net-snmp-config) + if test "X${SNMPCONFIG}" = "X"; then + AC_MSG_RESULT(You need the net_snmp development package to continue.) + enable_snmp=no + fi + fi + + if test $enable_snmp != no; then + AC_MSG_CHECKING(for special snmp libraries) + SNMPLIBS=`$SNMPCONFIG --libs` + AC_MSG_RESULT($SNMPLIBS) + fi + + if test $enable_snmp != no; then + savedLibs=$LIBS + LIBS="$LIBS $SNMPLIBS" + AC_CHECK_FUNCS(netsnmp_transport_open_client) + if test $ac_cv_func_netsnmp_transport_open_client != yes; then + AC_CHECK_FUNCS(netsnmp_tdomain_transport) + if test $ac_cv_func_netsnmp_tdomain_transport != yes; then + enable_snmp=no + fi + else + AC_DEFINE_UNQUOTED([NETSNMPV54], $NETSNMP_NEW_SUPPORT, [have net-snmp5.4 over]) + fi + LIBS=$savedLibs + fi + + AC_MSG_CHECKING(for snmp) + AC_MSG_RESULT($enable_snmp) + if test $enable_snmp = no; then + enable_snmp=0 + AC_MSG_ERROR(Unable to support SNMP) + else + enable_snmp=1 + PACKAGE_FEATURES="$PACKAGE_FEATURES snmp" + AC_DEFINE_UNQUOTED([ENABLE_SNMP], $enable_snmp, [Build in support for sending SNMP traps]) + fi +else + enable_snmp=0 +fi +AC_SUBST([SNMPLIBS]) +AC_SUBST([SNMP_LCRSO]) +AM_CONDITIONAL(BUILD_SNMP, test "${enable_snmp}" = "1") + # extra warnings EXTRA_WARNINGS="" @@ -521,6 +591,8 @@ AC_SUBST([OS_DYFLAGS]) AC_SUBST([OS_LDL]) AM_CONDITIONAL(INSTALL_TESTAGENTS, test -n "${enable_testagents}") +AM_CONDITIONAL(INSTALL_MIB, test "${enable_snmp}" = "1") +AM_CONDITIONAL(INSTALL_DBUSCONF, test "${enable_dbus}" = "1") AM_CONDITIONAL(AUGTOOL, test -n "${AUGTOOL}") AC_SUBST([NSS_LDFLAGS]) diff --git a/corosync.spec.in b/corosync.spec.in index 51f4404..aec13c6 100644 --- a/corosync.spec.in +++ b/corosync.spec.in @@ -8,6 +8,8 @@ %bcond_with testagents %bcond_with watchdog %bcond_with monitoring +%bcond_with snmp +%bcond_with dbus Name: corosync Summary: The Corosync Cluster Engine and Application Programming Interfaces @@ -35,6 +37,12 @@ BuildRequires: autoconf automake %endif BuildRequires: nss-devel BuildRequires: libibverbs-devel librdmacm-devel +%if %{with snmp} +BuildRequires: net-snmp-devel +%endif +%if %{with dbus} +BuildRequires: dbus-devel +%endif BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) @@ -61,6 +69,12 @@ export rdmacm_LIBS=-lrdmacm \ %if %{with monitoring} --enable-monitoring \ %endif +%if %{with snmp} + --enable-snmp \ +%endif +%if %{with dbus} + --enable-dbus \ +%endif --enable-rdma \ --with-initddir=%{_initrddir} @@ -107,13 +121,21 @@ fi %{_sbindir}/corosync-pload %{_sbindir}/corosync-cpgtool %{_sbindir}/corosync-quorumtool +%{_sbindir}/corosync-notifyd %{_bindir}/corosync-blackbox %dir %{_sysconfdir}/corosync %dir %{_sysconfdir}/corosync/service.d %dir %{_sysconfdir}/corosync/uidgid.d %config(noreplace) %{_sysconfdir}/corosync/corosync.conf.example %config(noreplace) %{_sysconfdir}/corosync/corosync.conf.example.udpu +%if %{with dbus} +%{_sysconfdir}/dbus-1/system.d/corosync-signals.conf +%endif +%if %{with snmp} +%(_datadir)/snmp/mibs/COROSYNC-MIB.txt +%endif %{_initrddir}/corosync +%{_initrddir}/corosync-notifyd %dir %{_libexecdir}/lcrso %{_libexecdir}/lcrso/coroparse.lcrso %{_libexecdir}/lcrso/objdb.lcrso @@ -143,6 +165,7 @@ fi %{_mandir}/man8/corosync-cpgtool.8* %{_mandir}/man8/corosync-fplay.8* %{_mandir}/man8/corosync-pload.8* +%{_mandir}/man8/corosync-notifyd.8* %{_mandir}/man8/corosync-quorumtool.8* %{_mandir}/man5/corosync.conf.5* diff --git a/cts/.gitignore b/cts/.gitignore index 6f99c8f..e9e3b0c 100644 --- a/cts/.gitignore +++ b/cts/.gitignore @@ -1,2 +1,3 @@ CTSvars.py *_test_agent +*.pyc diff --git a/init/.gitignore b/init/.gitignore index 6ab3892..0a75c32 100644 --- a/init/.gitignore +++ b/init/.gitignore @@ -1 +1,2 @@ generic +notifyd diff --git a/init/Makefile.am b/init/Makefile.am index 2d079d1..0ca9ee9 100644 --- a/init/Makefile.am +++ b/init/Makefile.am @@ -34,9 +34,9 @@ MAINTAINERCLEANFILES = Makefile.in -EXTRA_DIST = generic.in +EXTRA_DIST = generic.in notifyd.in -target_INIT = generic +target_INIT = generic notifyd %: %.in Makefile rm -f $@-t $@ @@ -57,7 +57,8 @@ clean-local: install-exec-local: $(INSTALL) -d $(DESTDIR)/$(INITDDIR) $(INSTALL) -m 755 generic $(DESTDIR)/$(INITDDIR)/corosync + $(INSTALL) -m 755 notifyd $(DESTDIR)/$(INITDDIR)/corosync-notifyd uninstall-local: cd $(DESTDIR)/$(INITDDIR) && \ - rm -f corosync + rm -f corosync corosync-notifyd diff --git a/init/notifyd.in b/init/notifyd.in new file mode 100755 index 0000000..85aa108 --- /dev/null +++ b/init/notifyd.in @@ -0,0 +1,147 @@ +#!/bin/bash + +# Authors: +# Angus Salkeld <[email protected]> +# +# License: Revised BSD + +# chkconfig: - 22 78 +# description: Corosync Dbus and snmp notifier +# processname: corosync-notifyd +# +### BEGIN INIT INFO +# Provides: corosync-notifyd +# Required-Start: $corosync +# Required-Stop: $corosync +# Default-Start: +# Default-Stop: +# Short-Description: Starts and stops Corosync Notifier. +# Description: Starts and stops Corosync Notifier. +### END INIT INFO + +desc="Corosync Notifier" +prog="corosync-notifyd" + +# set secure PATH +PATH="/sbin:/bin:/usr/sbin:/usr/bin:@SBINDIR@" + +success() +{ + echo -ne "[ OK ]\r" +} + +failure() +{ + echo -ne "[FAILED]\r" +} + +status() +{ + pid=$(pidof $1 2>/dev/null) + rtrn=$? + if [ $rtrn -ne 0 ]; then + echo "$1 is stopped" + else + echo "$1 (pid $pid) is running..." + fi + return $rtrn +} + +# rpm based distros +if [ -d @SYSCONFDIR@/sysconfig ]; then + [ -f @INITDDIR@/functions ] && . @INITDDIR@/functions + [ -f @SYSCONFDIR@/sysconfig/$prog ] && . @SYSCONFDIR@/sysconfig/$prog + [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/subsys/$prog" +fi + +# deb based distros +if [ -d @SYSCONFDIR@/default ]; then + [ -f @SYSCONFDIR@/default/$prog ] && . @SYSCONFDIR@/default/$prog + [ -z "$LOCK_FILE" ] && LOCK_FILE="@LOCALSTATEDIR@/lock/$prog" +fi + +# The version of __pids_pidof in /etc/init.d/functions calls pidof with -x +# This means it matches scripts, including this one. +# Redefine it here so that status (from the same file) works. +# Otherwise simultaneous calls to stop() will loop forever +__pids_pidof() { + pidof -c -o $$ -o $PPID -o %PPID "$1" || \ + pidof -c -o $$ -o $PPID -o %PPID "${1##*/}" +} + +start() +{ + echo -n "Starting $desc ($prog): " + + # most recent distributions use tmpfs for @LOCALSTATEDIR@/run + # to avoid to clean it up on every boot. + # they also assume that init scripts will create + # required subdirectories for proper operations + mkdir -p @LOCALSTATEDIR@/run + + if status $prog > /dev/null 2>&1; then + success + else + $prog $OPTIONS > /dev/null 2>&1 + + # give it time to fail + sleep 2 + if status $prog > /dev/null 2>&1; then + touch $LOCK_FILE + success + else + failure + rtrn=1 + fi + fi + echo +} + +stop() +{ + ! status $prog > /dev/null 2>&1 && return + + echo -n "Signaling $desc ($prog) to terminate: " + kill -TERM $(pidof $prog) > /dev/null 2>&1 + success + echo + + rm -f $LOCK_FILE + success + echo +} + +restart() +{ + stop + start +} + +rtrn=0 + +case "$1" in +start) + start +;; +restart|reload|force-reload) + restart +;; +condrestart|try-restart) + if status $prog > /dev/null 2>&1; then + restart + fi +;; +status) + status $prog + rtrn=$? +;; +stop) + stop +;; +*) + echo "usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + rtrn=2 +;; +esac + +exit $rtrn diff --git a/man/Makefile.am b/man/Makefile.am index 58923f0..33d1585 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -47,6 +47,7 @@ dist_man_MANS = \ corosync-cpgtool.8 \ corosync-fplay.8 \ corosync-pload.8 \ + corosync-notifyd.8 \ corosync-quorumtool.8 \ corosync_overview.8 \ cpg_overview.8 \ diff --git a/man/corosync-notifyd.8 b/man/corosync-notifyd.8 new file mode 100644 index 0000000..1ff8994 --- /dev/null +++ b/man/corosync-notifyd.8 @@ -0,0 +1,145 @@ +.\"/* +.\" * Copyright (C) 2010 Red Hat, Inc. +.\" * +.\" * All rights reserved. +.\" * +.\" * Author: Angus Salkeld <[email protected]> +.\" * +.\" * This software licensed under BSD license, the text of which follows: +.\" * +.\" * Redistribution and use in source and binary forms, with or without +.\" * modification, are permitted provided that the following conditions are met: +.\" * +.\" * - Redistributions of source code must retain the above copyright notice, +.\" * this list of conditions and the following disclaimer. +.\" * - Redistributions in binary form must reproduce the above copyright notice, +.\" * this list of conditions and the following disclaimer in the documentation +.\" * and/or other materials provided with the distribution. +.\" * - Neither the name of Red Hat, Inc. nor the names of its +.\" * contributors may be used to endorse or promote products derived from this +.\" * software without specific prior written permission. +.\" * +.\" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +.\" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +.\" * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +.\" * THE POSSIBILITY OF SUCH DAMAGE. +.\" */ +.TH COROSYNC-NOTIFYD 8 2011-01-14 +.SH NAME +corosync-notifyd \- Listen for important corosync events and send dbus and/or snmp traps. +.SH SYNOPSIS +.B "corosync-notifyd [\-f] [\-l] [\-o] [\-s] [\-m] [manager] [\-d] [-h]" +.SH DESCRIPTION +.B corosync-notifyd +uses corosync API to listen for important cluster events and can log them, +generate dbus signals or genterate snmp traps. +.SH OPTIONS +.TP +.B -f +Start application in foreground. +.TP +.B -l +Log all events. +.TP +.B -o +Print events to stdout (turns on -l). +.TP +.B -s +Send SNMP traps on all events. +.TP +.B -m +Set the SNMP manager address. +.TP +.B -d +Send DBUS signals on all events. +.TP +.B -h +Print this help +.SH EXAMPLES +.br +$ corosync-notifyd -o +.br +corosync-notifyd[18505]: troll[23374016] corosync-notify:18505:12 is now connected to corosync +.br +corosync-notifyd[18505]: troll[23374016] corosync-notify:18505:13 is now disconnected from corosync +.br +corosync-notifyd[18505]: troll[23374016] is now quorate +.br +corosync-notifyd[18505]: r2[1550100672] ip:192.168.100.92 joined +.br +corosync-notifyd[18505]: r2[1550100672] ip:192.168.100.92 left +.br + +.br +$ corosync-notifyd -o +.br + +Note this output below is from "dbus-monitor --system" + +.br +signal sender=:1.216 -> dest=(null destination) serial=2 path=/com/redhat/cluster/corosync; + interface=com.redhat.cluster.corosync; member=ConnectionStateChange +.br + string "troll" +.br + uint32 23374016 +.br + string "corosync-notify:18900:12" +.br + string "connected" +.br +signal sender=:1.216 -> dest=(null destination) serial=3 path=/com/redhat/cluster/corosync; + interface=com.redhat.cluster.corosync; member=ConnectionStateChange +.br + string "troll" +.br + uint32 23374016 +.br + string "corosync-notify:18900:13" +.br + string "disconnected" +.br +signal sender=:1.216 -> dest=(null destination) serial=4 path=/com/redhat/cluster/corosync; + interface=com.redhat.cluster.corosync; member=QorumStateChange +.br + string "troll" +.br + uint32 23374016 +.br + string "quorate" +.br +signal sender=:1.216 -> dest=(null destination) serial=5 path=/com/redhat/cluster/corosync; + interface=com.redhat.cluster.corosync; member=NodeStateChange +.br + string "r2" +.br + uint32 1550100672 +.br + string "192.168.100.92" +.br + string "joined" +.br +signal sender=:1.216 -> dest=(null destination) serial=6 path=/com/redhat/cluster/corosync; + interface=com.redhat.cluster.corosync; member=NodeStateChange +.br + string "r2" +.br + uint32 1550100672 +.br + string "192.168.100.92" +.br + string "left" +.SH SEE ALSO +.BR corosync (8), +.BR corosync-objctl (8), +.BR dbus-monitor (1), +.SH AUTHOR +Angus Salkeld +.PP diff --git a/tools/.gitignore b/tools/.gitignore index aa2eb2e..0dc0f7e 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -5,3 +5,4 @@ corosync-keygen corosync-objctl corosync-pload corosync-quorumtool +corosync-notifyd diff --git a/tools/Makefile.am b/tools/Makefile.am index 3a52f4a..2e97185 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -34,7 +34,8 @@ INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include sbin_PROGRAMS = corosync-fplay corosync-cfgtool \ corosync-keygen corosync-objctl \ - corosync-pload corosync-cpgtool corosync-quorumtool + corosync-pload corosync-cpgtool corosync-quorumtool \ + corosync-notifyd bin_SCRIPTS = corosync-blackbox @@ -52,5 +53,14 @@ corosync_quorumtool_LDADD = -lconfdb -lcfg -lquorum \ -lvotequorum -lcoroipcc ../lcr/liblcr.a corosync_quorumtool_LDFLAGS = -L../lib +corosync_notifyd_LDADD = -L../lib +corosync_notifyd_LDFLAGS = -lcfg -lconfdb ../lcr/liblcr.a -lcoroipcc \ + ../exec/coropoll.o $(DBUS_LIBS) $(SNMPLIBS) \ + -lquorum +corosync_notifyd_CPPFLAGS = $(DBUS_CFLAGS) + + lint: -splint $(LINT_FLAGS) $(CFLAGS) *.c + + diff --git a/tools/corosync-notifyd.c b/tools/corosync-notifyd.c new file mode 100644 index 0000000..d0565de --- /dev/null +++ b/tools/corosync-notifyd.c @@ -0,0 +1,1035 @@ +/* + * Copyright (c) 2011 Red Hat + * + * All rights reserved. + * + * Author: Angus Salkeld <[email protected]> + * + * This software licensed under BSD license, the text of which follows: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the MontaVista Software, Inc. nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <config.h> + +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/types.h> +#include <netdb.h> +#include <arpa/inet.h> + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <poll.h> +#include <signal.h> +#include <syslog.h> + +#include <corosync/corotypes.h> +#include <corosync/totem/coropoll.h> +#include <corosync/confdb.h> +#include <corosync/cfg.h> +#include <corosync/quorum.h> + +/* + * generic declarations + */ +enum { + CS_NTF_LOG, + CS_NTF_STDOUT, + CS_NTF_SNMP, + CS_NTF_DBUS, + CS_NTF_FG, + CS_NTF_MAX, +}; +static int conf[CS_NTF_MAX]; + +typedef void (*node_membership_fn_t)(char *nodename, uint32_t nodeid, char *state, char* ip); +typedef void (*node_quorum_fn_t)(char *nodename, uint32_t nodeid, const char *state); +typedef void (*application_connection_fn_t)(char *nodename, uint32_t nodeid, char *app_name, const char *state); + +struct notify_callbacks { + node_membership_fn_t node_membership_fn; + node_quorum_fn_t node_quorum_fn; + application_connection_fn_t application_connection_fn; +}; + +#define MAX_NOTIFIERS 5 +static int num_notifiers = 0; +static struct notify_callbacks notifiers[MAX_NOTIFIERS]; +static uint32_t local_nodeid = 0; +static char local_nodename[CS_MAX_NAME_LENGTH]; +static hdb_handle_t poll_handle; +static quorum_handle_t quorum_handle; + +static void _cs_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip); +static void _cs_node_quorum_event(const char *state); +static void _cs_application_connection_event(char *app_name, const char *state); + +#ifdef HAVE_DBUS +#include <dbus/dbus.h> +/* + * dbus/foghorn + */ +#define DBUS_CS_NAME "com.redhat.cluster.corosync" +#define DBUS_CS_IFACE "com.redhat.cluster.corosync" +#define DBUS_CS_PATH "/com/redhat/cluster/corosync" + +static DBusConnection *db = NULL; +static char _err[512]; +static int err_set = 0; +static void _cs_dbus_init(void); +#endif /* HAVE_DBUS */ + +#ifdef ENABLE_SNMP +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/snmpv3_api.h> +#include <net-snmp/agent/agent_trap.h> +#include <net-snmp/library/mib.h> +#include <net-snmp/library/snmp_api.h> +#include <net-snmp/library/snmp_client.h> +#include <net-snmp/library/snmp_debug.h> + +enum snmp_node_status { + SNMP_NODE_STATUS_UNKNOWN = 0, + SNMP_NODE_STATUS_JOINED = 1, + SNMP_NODE_STATUS_LEFT = 2 +}; + +#define SNMP_OID_COROSYNC "1.3.6.1.4.1.35488" +#define SNMP_OID_NOTICE_ROOT SNMP_OID_COROSYNC ".1" +#define SNMP_OID_NOTICE_NODE_TABLE SNMP_OID_NOTICE_ROOT ".1" +#define SNMP_OID_NOTICE_NODE_ENTRY SNMP_OID_NOTICE_NODE_TABLE ".1" +#define SNMP_OID_NOTICE_NODE_INDEX SNMP_OID_NOTICE_NODE_ENTRY ".1" +#define SNMP_OID_NOTICE_NODE_ID SNMP_OID_NOTICE_NODE_ENTRY ".2" +#define SNMP_OID_NOTICE_NODE SNMP_OID_NOTICE_NODE_ENTRY ".3" +#define SNMP_OID_NOTICE_NODE_STATE SNMP_OID_NOTICE_NODE_ENTRY ".4" + +#define SNMP_OID_TRAPS_ROOT SNMP_OID_COROSYNC ".100" +#define SNMP_OID_TRAPS_NODE SNMP_OID_TRAPS_ROOT ".1" + +static const char *local_host = "localhost"; +#endif /* ENABLE_SNMP */ +static char snmp_manager_buf[CS_MAX_NAME_LENGTH]; +static char *snmp_manager = NULL; + + +/* + * confdb + */ +#define SEPERATOR_STR "." + +static confdb_handle_t confdb_handle; + +static void _cs_confdb_key_changed(confdb_handle_t handle, + confdb_change_type_t change_type, + hdb_handle_t parent_object_handle, + hdb_handle_t object_handle, + const void *object_name, size_t object_name_len, + const void *key_name, size_t key_name_len, + const void *key_value, size_t key_value_len); + +static void _cs_confdb_object_created(confdb_handle_t handle, + hdb_handle_t parent_object_handle, + hdb_handle_t object_handle, + const void *name_pt, size_t name_len); + +static void _cs_confdb_object_deleted(confdb_handle_t handle, + hdb_handle_t parent_object_handle, + const void *name_pt, size_t name_len); + +static confdb_callbacks_t callbacks = { + .confdb_key_change_notify_fn = _cs_confdb_key_changed, + .confdb_object_create_change_notify_fn = _cs_confdb_object_created, + .confdb_object_delete_change_notify_fn = _cs_confdb_object_deleted, +}; + +static int32_t _cs_ip_to_hostname(char* ip, char* name_out) +{ + struct sockaddr_in sa; + int rc; + + if (strchr(ip, ':') == NULL) { + sa.sin_family = AF_INET; + } else { + sa.sin_family = AF_INET6; + } + + rc = inet_pton(sa.sin_family, ip, &sa.sin_addr); + if (rc == 0) { + return -EINVAL; + } + + rc = getnameinfo((struct sockaddr*)&sa, sizeof(sa), + name_out, CS_MAX_NAME_LENGTH, NULL, 0, 0); + if (rc != 0) { + syslog (LOG_ERR, "error looking up %s : %s\n", ip, gai_strerror(rc)); + return -EINVAL; + } + return 0; +} + +static void +_cs_confdb_key_changed(confdb_handle_t handle, + confdb_change_type_t change_type, + hdb_handle_t parent_object_handle, + hdb_handle_t object_handle, + const void *object_name_pt, size_t object_name_len, + const void *key_name_pt, size_t key_name_len, + const void *key_value_pt, size_t key_value_len) +{ + char parent_name[CS_MAX_NAME_LENGTH]; + size_t len = 0; + hdb_handle_t real_parent_object_handle; + cs_error_t rc = CS_OK; + char nodename[CS_MAX_NAME_LENGTH]; + char nodeid_str[CS_MAX_NAME_LENGTH]; + uint32_t nodeid; + char status[CS_MAX_NAME_LENGTH]; + char ip[CS_MAX_NAME_LENGTH]; + size_t ip_len; + confdb_value_types_t type; + char* open_bracket = NULL; + char* close_bracket = NULL; + + rc = confdb_object_parent_get (handle, + parent_object_handle, &real_parent_object_handle); + assert(rc == CS_OK); + + rc = confdb_object_name_get (handle, + real_parent_object_handle, + parent_name, + &len); + parent_name[len] = '\0'; + assert(rc == CS_OK); + + if (strcmp(parent_name, "members") == 0) { + if (strncmp(key_name_pt, "status", strlen("status")) == 0) { + + memcpy(nodeid_str, object_name_pt, object_name_len); + nodeid_str[object_name_len] = '\0'; + nodeid = atoi(nodeid_str); + + memcpy(status, key_value_pt, key_value_len); + status[key_value_len] = '\0'; + + rc = confdb_key_get_typed(handle, parent_object_handle, + "ip", ip, &ip_len, &type); + assert(rc == CS_OK); + ip[ip_len-1] = '\0'; + + /* + * We want the ip out of: "r(0) ip(192.168.100.92)" + */ + open_bracket = strrchr(ip, '('); + open_bracket++; + close_bracket = strrchr(open_bracket, ')'); + *close_bracket = '\0'; + _cs_ip_to_hostname(open_bracket, nodename); + + _cs_node_membership_event(nodename, nodeid, status, open_bracket); + } + } +} + +static void +_cs_confdb_object_created(confdb_handle_t handle, + hdb_handle_t parent_object_handle, + hdb_handle_t object_handle, + const void *name_pt, + size_t name_len) +{ + char parent_name[CS_MAX_NAME_LENGTH]; + size_t len = 0; + char obj_name[CS_MAX_NAME_LENGTH]; + hdb_handle_t real_parent_object_handle; + cs_error_t rc = CS_OK; + + memcpy(obj_name, name_pt, name_len); + obj_name[name_len] = '\0'; + + rc = confdb_object_parent_get (handle, + parent_object_handle, &real_parent_object_handle); + if (rc != CS_OK) { + /* this error is normally from our own cfg connection + * which is short lived. + */ + return; + } + + rc = confdb_object_name_get (handle, + real_parent_object_handle, parent_name, &len); + parent_name[len] = '\0'; + assert(rc == CS_OK); + + if (strcmp(parent_name, "connections") == 0) { + _cs_application_connection_event(obj_name, "connected"); + } +} + +static void +_cs_confdb_object_deleted(confdb_handle_t handle, + hdb_handle_t parent_object_handle, + const void *name_pt, + size_t name_len) +{ + char obj_name[CS_MAX_NAME_LENGTH]; + char parent_name[CS_MAX_NAME_LENGTH]; + size_t len = 0; + cs_error_t rc; + + memcpy(obj_name, name_pt, name_len); + obj_name[name_len] = '\0'; + + rc = confdb_object_name_get (handle, + parent_object_handle, parent_name, &len); + parent_name[len] = '\0'; + assert(rc == CS_OK); + + if (strcmp(parent_name, "connections") == 0) { + _cs_application_connection_event(obj_name, "disconnected"); + } +} + +static cs_error_t +_cs_confdb_find_object (confdb_handle_t handle, + const char * name_pt, + hdb_handle_t * out_handle) +{ + char * obj_name_pt; + char * save_pt; + hdb_handle_t obj_handle; + confdb_handle_t parent_object_handle = OBJECT_PARENT_HANDLE; + char tmp_name[CS_MAX_NAME_LENGTH]; + cs_error_t res = CS_OK; + + strncpy (tmp_name, name_pt, CS_MAX_NAME_LENGTH); + obj_name_pt = strtok_r(tmp_name, SEPERATOR_STR, &save_pt); + + while (obj_name_pt != NULL) { + res = confdb_object_find_start(handle, parent_object_handle); + if (res != CS_OK) { + syslog (LOG_ERR, "Could not start object_find %d\n", res); + exit (EXIT_FAILURE); + } + + res = confdb_object_find(handle, parent_object_handle, + obj_name_pt, strlen (obj_name_pt), &obj_handle); + if (res != CS_OK) { + return res; + } + + parent_object_handle = obj_handle; + obj_name_pt = strtok_r (NULL, SEPERATOR_STR, &save_pt); + } + + *out_handle = parent_object_handle; + return res; +} + +static int +_cs_confdb_dispatch(hdb_handle_t handle, + int fd, int revents, void *data) +{ + confdb_dispatch(confdb_handle, CONFDB_DISPATCH_ALL); + return 0; +} + +static void _cs_quorum_notification(quorum_handle_t handle, + uint32_t quorate, uint64_t ring_seq, + uint32_t view_list_entries, uint32_t *view_list) +{ + if (quorate) { + _cs_node_quorum_event("quorate"); + } else { + _cs_node_quorum_event("not quorate"); + } +} + +static int +_cs_quorum_dispatch(hdb_handle_t handle, + int fd, int revents, void *data) +{ + quorum_dispatch(quorum_handle, CS_DISPATCH_ALL); + return 0; +} + +static void +_cs_quorum_init(void) +{ + cs_error_t rc; + int fd; + + quorum_callbacks_t quorum_callbacks = { + .quorum_notify_fn = _cs_quorum_notification, + }; + + rc = quorum_initialize (&quorum_handle, &quorum_callbacks); + if (rc != CS_OK) { + syslog(LOG_ERR, "Could not connect to corosync(quorum)"); + return; + } + quorum_fd_get(quorum_handle, &fd); + poll_dispatch_add (poll_handle, fd, POLLIN|POLLNVAL, NULL, + _cs_quorum_dispatch); + quorum_trackstart(quorum_handle, CS_TRACK_CHANGES); +} + +static void +_cs_quorum_finalize(void) +{ + quorum_finalize (quorum_handle); +} + + +#ifdef HAVE_DBUS +/* + * Foghorn notifications + */ +static void +_cs_dbus_auto_flush(void) +{ + dbus_connection_ref(db); + dbus_connection_read_write(db, 500); + dbus_connection_unref(db); +} + +static void +_cs_dbus_release(void) +{ + DBusError err; + + if (!db) + return; + + dbus_error_init(&err); + dbus_bus_release_name(db, DBUS_CS_NAME, &err); + dbus_error_free(&err); + dbus_connection_unref(db); + db = NULL; +} + +static void +_cs_dbus_node_quorum_event(char *nodename, uint32_t nodeid, const char *state) +{ + DBusMessage *msg = NULL; + int ret = -1; + + if (err_set) { + syslog (LOG_ERR, "%s\n", _err); + err_set = 0; + } + + if (!db) { + goto out_free; + } + + if (dbus_connection_get_is_connected(db) != TRUE) { + err_set = 1; + snprintf(_err, sizeof(_err), "DBus connection lost"); + _cs_dbus_release(); + goto out_unlock; + } + + _cs_dbus_auto_flush(); + + if (!(msg = dbus_message_new_signal(DBUS_CS_PATH, + DBUS_CS_IFACE, + "QorumStateChange"))) { + syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__); + goto out_unlock; + } + + if (!dbus_message_append_args(msg, + DBUS_TYPE_STRING, &nodename, + DBUS_TYPE_UINT32, &nodeid, + DBUS_TYPE_STRING, &state, + DBUS_TYPE_INVALID)) { + syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__); + goto out_unlock; + } + + dbus_connection_send(db, msg, NULL); + ret = 0; + +out_unlock: + if (ret == -1) { + syslog (LOG_ERR, "%s() error\n", __func__); + } + if (msg) + dbus_message_unref(msg); +out_free: + return; +} + +static void +_cs_dbus_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip) +{ + DBusMessage *msg = NULL; + int ret = -1; + + if (err_set) { + syslog (LOG_ERR, "%s\n", _err); + err_set = 0; + } + + if (!db) { + goto out_free; + } + + if (dbus_connection_get_is_connected(db) != TRUE) { + err_set = 1; + snprintf(_err, sizeof(_err), "DBus connection lost"); + _cs_dbus_release(); + goto out_unlock; + } + + _cs_dbus_auto_flush(); + + if (!(msg = dbus_message_new_signal(DBUS_CS_PATH, + DBUS_CS_IFACE, + "NodeStateChange"))) { + syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__); + goto out_unlock; + } + + if (!dbus_message_append_args(msg, + DBUS_TYPE_STRING, &nodename, + DBUS_TYPE_UINT32, &nodeid, + DBUS_TYPE_STRING, &ip, + DBUS_TYPE_STRING, &state, + DBUS_TYPE_INVALID)) { + syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__); + goto out_unlock; + } + + dbus_connection_send(db, msg, NULL); + ret = 0; + +out_unlock: + if (ret == -1) { + syslog (LOG_ERR, "%s() error\n", __func__); + } + if (msg) + dbus_message_unref(msg); +out_free: + return; +} + +static void +_cs_dbus_application_connection_event(char *nodename, uint32_t nodeid, char *app_name, const char *state) +{ + DBusMessage *msg = NULL; + int ret = -1; + + if (err_set) { + syslog (LOG_ERR, "%s\n", _err); + err_set = 0; + } + + if (!db) { + goto out_free; + } + + if (dbus_connection_get_is_connected(db) != TRUE) { + err_set = 1; + snprintf(_err, sizeof(_err), "DBus connection lost"); + _cs_dbus_release(); + goto out_unlock; + } + + _cs_dbus_auto_flush(); + + if (!(msg = dbus_message_new_signal(DBUS_CS_PATH, + DBUS_CS_IFACE, + "ConnectionStateChange"))) { + syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__); + goto out_unlock; + } + + if (!dbus_message_append_args(msg, + DBUS_TYPE_STRING, &nodename, + DBUS_TYPE_UINT32, &nodeid, + DBUS_TYPE_STRING, &app_name, + DBUS_TYPE_STRING, &state, + DBUS_TYPE_INVALID)) { + syslog (LOG_ERR, "%s(%d) error\n", __func__, __LINE__); + goto out_unlock; + } + + dbus_connection_send(db, msg, NULL); + ret = 0; + +out_unlock: + if (msg) + dbus_message_unref(msg); +out_free: + return; +} + +static void +_cs_dbus_init(void) +{ + DBusConnection *dbc = NULL; + DBusError err; + + dbus_error_init(&err); + + dbc = dbus_bus_get(DBUS_BUS_SYSTEM, &err); + if (!dbc) { + snprintf(_err, sizeof(_err), + "dbus_bus_get: %s", err.message); + err_set = 1; + dbus_error_free(&err); + return; + } + + dbus_connection_set_exit_on_disconnect(dbc, FALSE); + + db = dbc; + + notifiers[num_notifiers].node_membership_fn = + _cs_dbus_node_membership_event; + notifiers[num_notifiers].node_quorum_fn = + _cs_dbus_node_quorum_event; + notifiers[num_notifiers].application_connection_fn = + _cs_dbus_application_connection_event; + num_notifiers++; +} + +#endif /* HAVE_DBUS */ + +#ifdef ENABLE_SNMP +static netsnmp_session *snmp_init (const char *target) +{ + static netsnmp_session *session = NULL; +#ifndef NETSNMPV54 + char default_port[128]; + snprintf (default_port, sizeof (default_port), "%s:162", target); +#endif + if (session) { + return (session); + } + + if (target == NULL) { + return NULL; + } + + session = malloc (sizeof (netsnmp_session)); + snmp_sess_init (session); + session->version = SNMP_VERSION_2c; + session->callback = NULL; + session->callback_magic = NULL; + + session = snmp_add(session, +#ifdef NETSNMPV54 + netsnmp_transport_open_client ("snmptrap", target), +#else + netsnmp_tdomain_transport (default_port, 0, "udp"), +#endif + NULL, NULL); + + if (session == NULL) { + syslog(LOG_ERR, "Could not create snmp transport"); + } + return (session); +} + +static inline void add_field ( + netsnmp_pdu *trap_pdu, + u_char asn_type, + const char *prefix, + void *value, + size_t value_size) +{ + oid _oid[MAX_OID_LEN]; + size_t _oid_len = MAX_OID_LEN; + if (snmp_parse_oid(prefix, _oid, &_oid_len)) { + snmp_pdu_add_variable (trap_pdu, _oid, _oid_len, asn_type, (u_char *) value, value_size); + } +} + +static void +_cs_snmp_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip) +{ + int ret; + char csysuptime[20]; + static oid snmptrap_oid[] = { 1,3,6,1,6,3,1,1,4,1,0 }; + static oid sysuptime_oid[] = { 1,3,6,1,2,1,1,3,0 }; + time_t now = time (NULL); + int node_status; + + netsnmp_pdu *trap_pdu; + netsnmp_session *session = snmp_init (snmp_manager); + if (session == NULL) { + syslog (LOG_NOTICE, "Failed to init SNMP session.\n"); + return ; + } + + trap_pdu = snmp_pdu_create (SNMP_MSG_TRAP2); + if (!trap_pdu) { + syslog (LOG_NOTICE, "Failed to create SNMP notification.\n"); + return ; + } + + if (strcmp(state, "joined") == 0) { + node_status = SNMP_NODE_STATUS_JOINED; + } else if (strcmp(state, "left") == 0) { + node_status = SNMP_NODE_STATUS_LEFT; + } else { + node_status = SNMP_NODE_STATUS_UNKNOWN; + } + + /* send uptime */ + sprintf (csysuptime, "%ld", now); + snmp_add_var (trap_pdu, sysuptime_oid, sizeof (sysuptime_oid) / sizeof (oid), 't', csysuptime); + snmp_add_var (trap_pdu, snmptrap_oid, sizeof (snmptrap_oid) / sizeof (oid), 'o', SNMP_OID_TRAPS_NODE); + + /* Add extries to the trap */ + add_field (trap_pdu, ASN_INTEGER, SNMP_OID_NOTICE_NODE_ID, (void*)&nodeid, sizeof (nodeid)); + add_field (trap_pdu, ASN_OCTET_STR, SNMP_OID_NOTICE_NODE, (void*)ip, strlen (ip)); + add_field (trap_pdu, ASN_INTEGER, SNMP_OID_NOTICE_NODE_STATE, (void*)&node_status, sizeof (node_status)); + + /* Send and cleanup */ + ret = snmp_send (session, trap_pdu); + if (ret == 0) { + /* error */ + syslog (LOG_ERR, "Could not send SNMP trap"); + snmp_free_pdu (trap_pdu); + } +} + +static void +_cs_snmp_init(void) +{ + if (snmp_manager == NULL) { + snmp_manager = (char*)local_host; + } + + notifiers[num_notifiers].node_membership_fn = + _cs_snmp_node_membership_event; + notifiers[num_notifiers].node_quorum_fn = NULL; + notifiers[num_notifiers].application_connection_fn = NULL; + num_notifiers++; +} + +#endif /* ENABLE_SNMP */ + +static void +_cs_syslog_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip) +{ + syslog (LOG_NOTICE, "%s[%d] ip:%s %s\n", nodename, nodeid, ip, state); +} + +static void +_cs_syslog_node_quorum_event(char *nodename, uint32_t nodeid, const char *state) +{ + if (strcmp(state, "quorate") == 0) { + syslog (LOG_NOTICE, "%s[%d] is now %s\n", nodename, nodeid, state); + } else { + syslog (LOG_NOTICE, "%s[%d] has lost quorum\n", nodename, nodeid); + } +} + +static void +_cs_syslog_application_connection_event(char *nodename, uint32_t nodeid, char* app_name, const char *state) +{ + if (strcmp(state, "connected") == 0) { + syslog (LOG_ERR, "%s[%d] %s is now %s to corosync\n", nodename, nodeid, app_name, state); + } else { + syslog (LOG_ERR, "%s[%d] %s is now %s from corosync\n", nodename, nodeid, app_name, state); + } +} + +static void +_cs_node_membership_event(char *nodename, uint32_t nodeid, char *state, char* ip) +{ + int i; + + for (i = 0; i < num_notifiers; i++) { + if (notifiers[i].node_membership_fn) { + notifiers[i].node_membership_fn(nodename, nodeid, state, ip); + } + } +} + +static void +_cs_local_node_info_get(char **nodename, uint32_t *nodeid) +{ + cs_error_t rc; + corosync_cfg_handle_t cfg_handle; + + if (local_nodeid == 0) { + corosync_cfg_initialize(&cfg_handle, NULL); + rc = corosync_cfg_local_get (cfg_handle, &local_nodeid); + corosync_cfg_finalize(cfg_handle); + if (rc != CS_OK) { + local_nodeid = 0; + strcpy(local_nodename, "localhost"); + } else { + gethostname(local_nodename, CS_MAX_NAME_LENGTH); + } + } + *nodeid = local_nodeid; + *nodename = local_nodename; +} + +static void +_cs_node_quorum_event(const char *state) +{ + int i; + char *nodename; + uint32_t nodeid; + + _cs_local_node_info_get(&nodename, &nodeid); + + for (i = 0; i < num_notifiers; i++) { + if (notifiers[i].node_quorum_fn) { + notifiers[i].node_quorum_fn(nodename, nodeid, state); + } + } +} + +static void +_cs_application_connection_event(char *app_name, const char *state) +{ + int i; + char *nodename; + uint32_t nodeid; + + _cs_local_node_info_get(&nodename, &nodeid); + + for (i = 0; i < num_notifiers; i++) { + if (notifiers[i].application_connection_fn) { + notifiers[i].application_connection_fn(nodename, nodeid, app_name, state); + } + } +} + +static void +sig_exit_handler (int num) +{ + poll_stop(poll_handle); +} + +static void +_cs_confdb_init(void) +{ + hdb_handle_t obj_handle; + cs_error_t rc; + int conf_fd = 0; + + rc = confdb_initialize (&confdb_handle, &callbacks); + if (rc != CS_OK) { + syslog (LOG_ERR, "Failed to initialize the objdb API. Error %d\n", rc); + exit (EXIT_FAILURE); + } + confdb_fd_get(confdb_handle, &conf_fd); + + poll_dispatch_add (poll_handle, conf_fd, POLLIN|POLLNVAL, NULL, + _cs_confdb_dispatch); + + rc = _cs_confdb_find_object (confdb_handle, "runtime.connections.", + &obj_handle); + if (rc != CS_OK) { + syslog (LOG_ERR, + "Failed to find the connections object. Error %d\n", rc); + exit (EXIT_FAILURE); + } + + rc = confdb_track_changes (confdb_handle, obj_handle, + CONFDB_TRACK_DEPTH_ONE); + if (rc != CS_OK) { + syslog (LOG_ERR, + "Failed to track the connections object. Error %d\n", rc); + exit (EXIT_FAILURE); + } + rc = _cs_confdb_find_object(confdb_handle, + "runtime.totem.pg.mrp.srp.members.", &obj_handle); + if (rc != CS_OK) { + syslog (LOG_ERR, "Failed to find the object. Error %d\n", rc); + exit (EXIT_FAILURE); + } + + rc = confdb_track_changes(confdb_handle, + obj_handle, CONFDB_TRACK_DEPTH_RECURSIVE); + if (rc != CS_OK) { + syslog (LOG_ERR, + "Failed to track the object. Error %d\n", rc); + exit (EXIT_FAILURE); + } +} + +static void +_cs_confdb_finalize(void) +{ + confdb_stop_track_changes (confdb_handle); + confdb_finalize (confdb_handle); +} + +static void +_cs_check_config(void) +{ + if (conf[CS_NTF_LOG] == 0 && + conf[CS_NTF_STDOUT] == 0 && + conf[CS_NTF_SNMP] == 0 && + conf[CS_NTF_DBUS] == 0) { + syslog(LOG_ERR, "no event type enabled, see corosync-notifyd -h, exiting."); + exit(EXIT_FAILURE); + } + +#ifndef ENABLE_SNMP + syslog(LOG_ERR, "Not compiled with Snmp support enabled, exiting."); + exit(EXIT_FAILURE); +#endif +#ifndef HAVE_DBUS + syslog(LOG_ERR, "Not compiled with Dbus support enabled, exiting."); + exit(EXIT_FAILURE); +#endif + + if (conf[CS_NTF_STDOUT] && !conf[CS_NTF_FG]) { + syslog(LOG_ERR, "configured to print to stdout and run in the background, exiting"); + exit(EXIT_FAILURE); + } + if (conf[CS_NTF_SNMP] && conf[CS_NTF_DBUS]) { + syslog(LOG_ERR, "configured to send snmp traps and dbus signals - are you sure?."); + } +} + +static void +_cs_usage(void) +{ + fprintf(stderr, "usage:\n"\ + " -f : Start application in foreground.\n"\ + " -l : Log all events.\n"\ + " -o : Print events to stdout (turns on -l).\n"\ + " -s : Send SNMP traps on all events.\n"\ + " -m : SNMP Manager IP address (defaults to localhost).\n"\ + " -d : Send DBUS signals on all events.\n"\ + " -h : Print this help\n\n"); +} + +int +main(int argc, char *argv[]) +{ + int ch; + + conf[CS_NTF_FG] = 0; + conf[CS_NTF_LOG] = 0; + conf[CS_NTF_STDOUT] = 0; + conf[CS_NTF_SNMP] = 0; + conf[CS_NTF_DBUS] = 0; + + while ((ch = getopt (argc, argv, "floshdm:")) != EOF) { + switch (ch) { + case 'f': + conf[CS_NTF_FG] = 1; + break; + case 'l': + conf[CS_NTF_LOG] = 1; + break; + case 'm': + conf[CS_NTF_SNMP] = 1; + strcpy(snmp_manager_buf, optarg); + snmp_manager = snmp_manager_buf; + break; + case 'o': + conf[CS_NTF_LOG] = 1; + conf[CS_NTF_STDOUT] = 1; + break; + case 's': + conf[CS_NTF_SNMP] = 1; + break; + case 'd': + conf[CS_NTF_DBUS] = 1; + break; + case 'h': + default: + _cs_usage(); + return EXIT_FAILURE; + } + } + + if (conf[CS_NTF_STDOUT]) { + openlog(NULL, LOG_PID|LOG_PERROR, LOG_DAEMON); + } else { + openlog(NULL, LOG_PID, LOG_DAEMON); + } + _cs_check_config(); + + if (!conf[CS_NTF_FG]) { + daemon(0, 0); + } + + num_notifiers = 0; + if (conf[CS_NTF_LOG]) { + notifiers[num_notifiers].node_membership_fn = + _cs_syslog_node_membership_event; + notifiers[num_notifiers].node_quorum_fn = + _cs_syslog_node_quorum_event; + notifiers[num_notifiers].application_connection_fn = + _cs_syslog_application_connection_event; + num_notifiers++; + } + + poll_handle = poll_create(); + + _cs_confdb_init(); + _cs_quorum_init(); + +#ifdef HAVE_DBUS + if (conf[CS_NTF_DBUS]) { + _cs_dbus_init(); + } +#endif /* HAVE_DBUS */ + +#ifdef ENABLE_SNMP + if (conf[CS_NTF_SNMP]) { + _cs_snmp_init(); + } +#endif /* ENABLE_SNMP */ + + (void)signal (SIGINT, sig_exit_handler); + (void)signal (SIGQUIT, sig_exit_handler); + (void)signal (SIGTERM, sig_exit_handler); + + poll_run(poll_handle); + +#ifdef HAVE_DBUS + if (conf[CS_NTF_DBUS]) { + _cs_dbus_release(); + } +#endif /* HAVE_DBUS */ + + _cs_quorum_finalize(); + _cs_confdb_finalize(); + + return 0; +} + -- 1.7.3.4 _______________________________________________ Openais mailing list [email protected] https://lists.linux-foundation.org/mailman/listinfo/openais
