[libvirt] [libvirt-snmp][PATCH v3 3/3] Add SNMP trap/notification support.

Michal Privoznik mprivozn at redhat.com
Wed Mar 23 10:06:08 UTC 2011


This patch adds support for domain lifecycle notification support
over SNMP traps. SNMP subagent monitors any domain events and when
something interesting happens, it sends a trap.

Monitoring is done in a joinable thread using polling (used
domain-events example from libvirt) so we won't block the agent itself.

Some debug info can be printed out by setting LIBVIRT_SNMP_VERBOSE
environment variable.
---
 INSTALL.1st                |    9 +-
 configure.ac               |    9 ++-
 libvirt-snmp.spec.in       |    5 +-
 src/Makefile.am            |    1 +
 src/README.txt             |    2 +
 src/libvirtNotifications.c |   16 +++-
 src/libvirtNotifications.h |   16 +++-
 src/libvirtSnmp.c          |  277 +++++++++++++++++++++++++++++++++++++++++++-
 8 files changed, 322 insertions(+), 13 deletions(-)

diff --git a/INSTALL.1st b/INSTALL.1st
index 31345d85d91841bf7ef55f41e932360ea5762133..c439bf3a8cce4db7dea48c36541905196c194493 100644
--- a/INSTALL.1st
+++ b/INSTALL.1st
@@ -15,14 +15,17 @@ Now it's time for make:
     make
     su -c "make install"
 
-This compile all sources producing runable SNMP subagent
-libvirtMib_subagent, which is installed right after.
+This compiles all source producing a runnable SNMP subagent,
+libvirtMib_subagent, which is installed afterward.
 But before we run it, we need to edit /etc/snmp/snmpd.conf
-so it contains this two lines:
+so it contains these four lines:
 
 rwcommunity public
 master agentx
 
+trap2sink  localhost
+trapcommunity public
+
 and then restart snmpd:
     /etc/init.d/snmpd restart
 
diff --git a/configure.ac b/configure.ac
index dcab0aea21b965e03de82181a779aee3dac8d9bb..7cff5f202364afc593c1656c529379882ace5a1f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-AC_INIT([libvirt-snmp],[0.0.1],[libvir-list at redhat.com],[],[http://libvirt.org])
+AC_INIT([libvirt-snmp],[0.0.2],[libvir-list at redhat.com],[],[http://libvirt.org])
 AM_INIT_AUTOMAKE([-Wall -Werror])
 AC_CONFIG_HEADERS([config.h])
 
@@ -86,5 +86,12 @@ fi
 
 AC_SUBST([MIB_DIR])
 
+dnl pthread
+PTHREAD_LIBS=
+AC_CHECK_HEADERS(pthread.h, [], [AC_MSG_ERROR([pthread.h required])])
+AC_CHECK_LIB(pthread, pthread_create, [PTHREAD_LIBS="-lpthread"])
+
+AC_SUBST([PTHREAD_LIBS])
+
 AC_OUTPUT(Makefile src/Makefile docs/Makefile libvirt-snmp.spec)
 
diff --git a/libvirt-snmp.spec.in b/libvirt-snmp.spec.in
index bbc5602a4b914ae73f951ed979229ef7c62c7ae2..02b7164257a0bed43d3d285697d50782e50eab25 100644
--- a/libvirt-snmp.spec.in
+++ b/libvirt-snmp.spec.in
@@ -1,6 +1,6 @@
 Name:		libvirt-snmp
 Version:	@VERSION@
-Release:	3%{?dist}%{?extra_release}
+Release:	1%{?dist}%{?extra_release}
 Summary:	SNMP functionality for libvirt
 
 Group:		Development/Libraries
@@ -36,6 +36,9 @@ make install DESTDIR=$RPM_BUILD_ROOT
 
 
 %changelog
+* Wed Mar 23 2011 Michal Privoznik <mprivozn at redhat.com> 0.0.2-1
+- add SNMP trap/notification support
+
 * Fri Mar 11 2011 Michal Privoznik <mprivozn at redhat.com> 0.0.1-3
 - remove LIBVIRT-MIB.txt from %doc
 
diff --git a/src/Makefile.am b/src/Makefile.am
index d68f17499d8768706050a7f38f77fc597fb0db46..8aa4cd5975a46c640d9d612f94567b63e04f8569 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -9,6 +9,7 @@ AM_CFLAGS =	\
 
 AM_LDFLAGS = 	\
 		$(COVERAGE_LDFLAGS) \
+		$(PTHREAD_LIBS) \
 		$(SNMP_LIBS)
 
 USER_SRCS = 	\
diff --git a/src/README.txt b/src/README.txt
index 6d010f6d0fedfa6e47333468bfa9dec40e47d29c..5e9823a51b4facc8e0ce322d0c2201010ba7e207 100644
--- a/src/README.txt
+++ b/src/README.txt
@@ -47,6 +47,8 @@ $ make
 2. use following /etc/snmp/snmpd.conf:
 rwcommunity public
 master agentx
+trap2sink  localhost
+trapcommunity public
 
 3. service snmpd start
 
diff --git a/src/libvirtNotifications.c b/src/libvirtNotifications.c
index 0001425b0c1c338bca01d958320ee2379d792866..c235638b1f49a243f55c67523433b55d624925c1 100644
--- a/src/libvirtNotifications.c
+++ b/src/libvirtNotifications.c
@@ -3,9 +3,21 @@
  *
  * Copyright (C) 2011 Red Hat, Inc.
  *
- * See COPYING for the license of this software
+ * 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.
  *
- * Michal Privoznik <mprivozn at redhat.com>
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Michal Privoznik <mprivozn at redhat.com>
  */
 
 #include <net-snmp/net-snmp-config.h>
diff --git a/src/libvirtNotifications.h b/src/libvirtNotifications.h
index 84eefc32359320d9a3cb501a7941241731246dc2..0be8fdc2193af60d01fca992158e312844215e86 100644
--- a/src/libvirtNotifications.h
+++ b/src/libvirtNotifications.h
@@ -3,9 +3,21 @@
  *
  * Copyright (C) 2011 Red Hat, Inc.
  *
- * See COPYING for the license of this software
+ * 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.
  *
- * Michal Privoznik <mprivozn at redhat.com>
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * Author: Michal Privoznik <mprivozn at redhat.com>
  */
 
 #ifndef LIBVIRTNOTIFICATIONS_H
diff --git a/src/libvirtSnmp.c b/src/libvirtSnmp.c
index dd1bd33dc3fbc08656d09075a3c1a1741ebc002f..1bc947d85384e2ced111cde301951b4e5c3666f6 100644
--- a/src/libvirtSnmp.c
+++ b/src/libvirtSnmp.c
@@ -22,12 +22,168 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include <signal.h>
 
 #include "libvirtSnmp.h"
-/* include our MIB structures*/
-#include "libvirtGuestTable.h"
+#include "libvirtGuestTable.h"      /* include our MIB structures*/
+#include "libvirtNotifications.h"
 
+#define DEBUG0(fmt) if (verbose) printf("%s:%d :: " fmt "\n", \
+        __func__, __LINE__)
+#define DEBUG(fmt, ...) if (verbose) printf("%s:%d: " fmt "\n", \
+        __func__, __LINE__, __VA_ARGS__)
+#define STREQ(a,b) (strcmp(a,b) == 0)
+
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((__unused__))
+#endif
+
+int verbose = 0;
 virConnectPtr conn;
+int callbackRet = -1;
+int run = 1;
+pthread_t poll_thread;
+
+/* handle globals */
+int h_fd = 0;
+virEventHandleType h_event = 0;
+virEventHandleCallback h_cb = NULL;
+virFreeCallback h_ff = NULL;
+void *h_opaque = NULL;
+
+/* timeout globals */
+#define TIMEOUT_MS 1000
+int t_active = 0;
+int t_timeout = -1;
+virEventTimeoutCallback t_cb = NULL;
+virFreeCallback t_ff = NULL;
+void *t_opaque = NULL;
+
+
+static int
+domainEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED,
+                    virDomainPtr dom, int event, int detail,
+                    void *opaque ATTRIBUTE_UNUSED)
+{
+    DEBUG("%s EVENT: Domain %s(%d) %d %d\n", __func__,
+          virDomainGetName(dom), virDomainGetID(dom), event, detail);
+
+    send_libvirtGuestNotif_trap(dom);
+    return 0;
+}
+
+static void
+myFreeFunc(void *opaque)
+{
+    if (opaque)
+        free(opaque);
+}
+
+/* EventImpl Functions */
+int
+myEventHandleTypeToPollEvent(virEventHandleType events)
+{
+    int ret = 0;
+
+    if (events & VIR_EVENT_HANDLE_READABLE)
+        ret |= POLLIN;
+    if (events & VIR_EVENT_HANDLE_WRITABLE)
+        ret |= POLLOUT;
+    if (events & VIR_EVENT_HANDLE_ERROR)
+        ret |= POLLERR;
+    if (events & VIR_EVENT_HANDLE_HANGUP)
+        ret |= POLLHUP;
+    return ret;
+}
+
+virEventHandleType
+myPollEventToEventHandleType(int events)
+{
+    virEventHandleType ret = 0;
+
+    if (events & POLLIN)
+        ret |= VIR_EVENT_HANDLE_READABLE;
+    if (events & POLLOUT)
+        ret |= VIR_EVENT_HANDLE_WRITABLE;
+    if (events & POLLERR)
+        ret |= VIR_EVENT_HANDLE_ERROR;
+    if (events & POLLHUP)
+        ret |= VIR_EVENT_HANDLE_HANGUP;
+    return ret;
+}
+
+int
+myEventAddHandleFunc(int fd, int event,
+                     virEventHandleCallback cb,
+                     void *opaque, virFreeCallback ff)
+{
+    DEBUG("Add handle %d %d %p %p", fd, event, cb, opaque);
+    h_fd = fd;
+    h_event = myEventHandleTypeToPollEvent(event);
+    h_cb = cb;
+    h_ff = ff;
+    h_opaque = opaque;
+    return 0;
+}
+
+void
+myEventUpdateHandleFunc(int fd, int event)
+{
+    DEBUG("Updated Handle %d %d", fd, event);
+    h_event = myEventHandleTypeToPollEvent(event);
+    return;
+}
+
+int
+myEventRemoveHandleFunc(int fd)
+{
+    DEBUG("Removed Handle %d", fd);
+    h_fd = 0;
+    if (h_ff)
+        (h_ff) (h_opaque);
+    return 0;
+}
+
+int
+myEventAddTimeoutFunc(int timeout,
+                      virEventTimeoutCallback cb,
+                      void *opaque, virFreeCallback ff)
+{
+    DEBUG("Adding Timeout %d %p %p", timeout, cb, opaque);
+    t_active = 1;
+    t_timeout = timeout;
+    t_cb = cb;
+    t_ff = ff;
+    t_opaque = opaque;
+    return 0;
+}
+
+void
+myEventUpdateTimeoutFunc(int timer ATTRIBUTE_UNUSED, int timeout)
+{
+    /*DEBUG("Timeout updated %d %d", timer, timeout); */
+    t_timeout = timeout;
+}
+
+int
+myEventRemoveTimeoutFunc(int timer)
+{
+    DEBUG("Timeout removed %d", timer);
+    t_active = 0;
+    if (t_ff)
+        (t_ff) (t_opaque);
+    return 0;
+}
+
+/* Signal trap function */
+static void
+stop(int sig)
+{
+    run = 0;
+}
 
 static void
 showError(virConnectPtr conn)
@@ -188,10 +344,113 @@ out:
     return ret;
 }
 
+/* Polling thread function */
+void *
+pollingThreadFunc(void *foo) {
+    int sts;
+
+    while (run) {
+        struct pollfd pfd = {.fd = h_fd,
+            .events = h_event,
+            .revents = 0
+        };
+
+        sts = poll(&pfd, 1, TIMEOUT_MS);
+
+        /* if t_timeout < 0 then t_cb must not be called */
+        if (t_cb && t_active && t_timeout >= 0) {
+            t_cb(t_timeout, t_opaque);
+        }
+
+        if (sts == 0) {
+            /* DEBUG0("Poll timeout"); */
+            continue;
+        }
+        if (sts < 0) {
+            DEBUG0("Poll failed");
+            continue;
+        }
+        if (pfd.revents & POLLHUP) {
+            DEBUG0("Reset by peer");
+            pthread_exit(NULL);
+        }
+
+        if (h_cb) {
+            h_cb(0,
+                 h_fd,
+                 myPollEventToEventHandleType(pfd.revents & h_event),
+                 h_opaque);
+        }
+
+    }
+
+    pthread_exit(NULL);
+}
+
+/* Function to register domain lifecycle events collection */
+int
+libvirtRegisterEvents(virConnectPtr conn) {
+    struct sigaction action_stop;
+    pthread_attr_t thread_attr;
+
+    memset(&action_stop, 0, sizeof action_stop);
+
+    action_stop.sa_handler = stop;
+
+    sigaction(SIGTERM, &action_stop, NULL);
+    sigaction(SIGINT, &action_stop, NULL);
+
+    DEBUG0("Registering domain event callback");
+
+    callbackRet = virConnectDomainEventRegisterAny(conn, NULL,
+                                                   VIR_DOMAIN_EVENT_ID_LIFECYCLE,
+                                                   VIR_DOMAIN_EVENT_CALLBACK
+                                                   (domainEventCallback),
+                                                   NULL, myFreeFunc);
+
+    if (callbackRet == -1)
+        return -1;
+
+    /* we need a thread to poll for events */
+    pthread_attr_init(&thread_attr);
+    pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE);
+
+    if (pthread_create
+        (&poll_thread, &thread_attr, pollingThreadFunc, NULL))
+        return -1;
+
+    pthread_attr_destroy(&thread_attr);
+
+    return 0;
+}
+
+/* Unregister domain events collection */
+int
+libvirtUnregisterEvents(virConnectPtr conn)
+{
+    void *status;
+
+    pthread_join(poll_thread, &status);
+    virConnectDomainEventDeregisterAny(conn, callbackRet);
+    callbackRet = -1;
+    return 0;
+}
+
 int libvirtSnmpInit(void)
 {
-    /* virConnectOpenAuth is called here with all default parameters,
-     * except, possibly, the URI of the hypervisor. */
+    char *verbose_env = getenv("LIBVIRT_SNMP_VERBOSE");
+
+    verbose = verbose_env != NULL;
+
+    /* if we don't already have registered callback */
+    if (callbackRet == -1)
+        virEventRegisterImpl(myEventAddHandleFunc,
+                             myEventUpdateHandleFunc,
+                             myEventRemoveHandleFunc,
+                             myEventAddTimeoutFunc,
+                             myEventUpdateTimeoutFunc,
+                             myEventRemoveTimeoutFunc);
+
     /* TODO: configure the URI */
     /* Use libvirt env variable LIBVIRT_DEFAULT_URI by default*/
     conn = virConnectOpenAuth(NULL, virConnectAuthPtrDefault, 0);
@@ -201,11 +460,21 @@ int libvirtSnmpInit(void)
         showError(conn);
         return -1;
     }
+
+    if ((callbackRet == -1) && libvirtRegisterEvents(conn)) {
+        printf("Unable to register domain events\n");
+        return -1;
+    }
+
     return 0;
 }
 
 void libvirtSnmpShutdown(void)
 {
+    if (libvirtUnregisterEvents(conn)) {
+        printf("Failed to unregister domain events\n");
+    }
+
     if (0 != virConnectClose(conn)) {
         printf("Failed to disconnect from hypervisor\n");
         showError(conn);
-- 
1.7.4




More information about the libvir-list mailing list