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@xxxxxxxxxx],[],[http://libvirt.org]) +AC_INIT([libvirt-snmp],[0.0.2],[libvir-list@xxxxxxxxxx],[],[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@xxxxxxxxxx> 0.0.2-1 +- add SNMP trap/notification support + * Fri Mar 11 2011 Michal Privoznik <mprivozn@xxxxxxxxxx> 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@xxxxxxxxxx> + * 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@xxxxxxxxxx> */ #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@xxxxxxxxxx> + * 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@xxxxxxxxxx> */ #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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list