diff --git a/components/Makefile.am b/components/Makefile.am index 9a9308a8c..6523d87b5 100644 --- a/components/Makefile.am +++ b/components/Makefile.am @@ -8,4 +8,5 @@ SUBDIRS = \ demo \ db_ido_mysql \ livestatus \ - notification + notification \ + perfdata diff --git a/components/perfdata/.gitignore b/components/perfdata/.gitignore new file mode 100644 index 000000000..80a2bcc22 --- /dev/null +++ b/components/perfdata/.gitignore @@ -0,0 +1 @@ +perfdata-type.cpp diff --git a/components/perfdata/Makefile.am b/components/perfdata/Makefile.am new file mode 100644 index 000000000..d27d84762 --- /dev/null +++ b/components/perfdata/Makefile.am @@ -0,0 +1,37 @@ +## Process this file with automake to produce Makefile.in + + +pkglib_LTLIBRARIES = \ + libperfdata.la + +CLEANFILES = \ + perfdata-type.cpp + +.conf.cpp: $(top_builddir)/tools/mkembedconfig/mkembedconfig + $(top_builddir)/tools/mkembedconfig/mkembedconfig $< $@ + +libperfdata_la_SOURCES = \ + graphitewriter.cpp \ + graphitewriter.h \ + perfdatawriter.cpp \ + perfdatawriter.h \ + perfdata-type.conf + +libperfdata_la_CPPFLAGS = \ + $(LTDLINCL) \ + $(BOOST_CPPFLAGS) \ + -I${top_srcdir}/lib \ + -I${top_srcdir}/components + +libperfdata_la_LDFLAGS = \ + $(BOOST_LDFLAGS) \ + -no-undefined \ + @RELEASE_INFO@ \ + @VERSION_INFO@ + +libperfdata_la_LIBADD = \ + $(BOOST_THREAD_LIB) \ + $(BOOST_SYSTEM_LIB) \ + ${top_builddir}/lib/base/libbase.la \ + ${top_builddir}/lib/config/libconfig.la \ + ${top_builddir}/lib/icinga/libicinga.la diff --git a/components/perfdata/graphitewriter.cpp b/components/perfdata/graphitewriter.cpp new file mode 100644 index 000000000..4b83004cf --- /dev/null +++ b/components/perfdata/graphitewriter.cpp @@ -0,0 +1,219 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2013 Icinga Development Team (http://www.icinga.org/) * + * * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#include "perfdata/graphitewriter.h" +#include "icinga/service.h" +#include "icinga/macroprocessor.h" +#include "icinga/icingaapplication.h" +#include "icinga/compatutility.h" +#include "base/tcpsocket.h" +#include "base/dynamictype.h" +#include "base/objectlock.h" +#include "base/logger_fwd.h" +#include "base/convert.h" +#include "base/utility.h" +#include "base/application.h" +#include "base/stream.h" +#include "base/networkstream.h" +#include "base/bufferedstream.h" +#include +#include +#include +#include + +using namespace icinga; + +REGISTER_TYPE(GraphiteWriter); + +void GraphiteWriter::Start(void) +{ + DynamicObject::Start(); + + m_ReconnectTimer = boost::make_shared(); + m_ReconnectTimer->SetInterval(10); + m_ReconnectTimer->OnTimerExpired.connect(boost::bind(&GraphiteWriter::ReconnectTimerHandler, this)); + m_ReconnectTimer->Start(); + m_ReconnectTimer->Reschedule(0); + + Service::OnNewCheckResult.connect(boost::bind(&GraphiteWriter::CheckResultHandler, this, _1, _2)); +} + +String GraphiteWriter::GetHost(void) const +{ + if (m_Host.IsEmpty()) + return "127.0.0.1"; + else + return m_Host; +} + +String GraphiteWriter::GetPort(void) const +{ + if (m_Port.IsEmpty()) + return "2003"; + else + return m_Port; +} + +void GraphiteWriter::ReconnectTimerHandler(void) +{ + /* TODO try to write the stream, and catch the exception - not connected */ + if (m_Stream) + return; + + TcpSocket::Ptr socket = boost::make_shared(); + + Log(LogInformation, "icinga", "GraphiteWriter: Reconnect to tcp socket on host '" + GetHost() + "' port '" + GetPort() + "'."); + socket->Connect(GetHost(), GetPort()); + + NetworkStream::Ptr net_stream = boost::make_shared(socket); + m_Stream = boost::make_shared(net_stream); +} + +void GraphiteWriter::CheckResultHandler(const Service::Ptr& service, const Dictionary::Ptr& cr) +{ + if (!IcingaApplication::GetInstance()->GetEnablePerfdata()) + return; + + Host::Ptr host = service->GetHost(); + + if (!host) + return; + + /* service metrics */ + std::vector metrics; + String metricName; + Value metricValue; + + /* basic metrics */ + AddServiceMetric(metrics, service, "current_attempt", service->GetCurrentCheckAttempt()); + AddServiceMetric(metrics, service, "max_check_attempts", service->GetMaxCheckAttempts()); + AddServiceMetric(metrics, service, "state_type", service->GetStateType()); + AddServiceMetric(metrics, service, "state", service->GetState()); + AddServiceMetric(metrics, service, "latency", Service::CalculateLatency(cr)); + AddServiceMetric(metrics, service, "execution_time", Service::CalculateExecutionTime(cr)); + + /* performance data metrics */ + String perfdata = CompatUtility::GetCheckResultPerfdata(cr); + + if (!perfdata.IsEmpty()) { + Log(LogDebug, "icinga", "GraphiteWriter: Processing perfdata: '" + perfdata + "'."); + + /* + * 'foo bar'=0;;; baz=0.0;;; + * 'label'=value[UOM];[warn];[crit];[min];[max] + */ + std::vector tokens; + boost::algorithm::split(tokens, perfdata, boost::is_any_of(" ")); + + /* TODO deal with 'foo bar'=0;;; 'baz'=1.0;;; */ + BOOST_FOREACH(const String& token, tokens) { + std::vector key_val; + boost::algorithm::split(key_val, token, boost::is_any_of("=")); + + if (key_val.size() == 0) { + Log(LogWarning, "icinga", "GraphiteWriter: Invalid performance data: '" + token + "'."); + return; + } + + String metricName = key_val[0]; + + if (key_val.size() == 1) { + Log(LogWarning, "icinga", "GraphiteWriter: Invalid performance data: '" + token + "'."); + return; + } + + std::vector perfdata_values; + boost::algorithm::split(perfdata_values, key_val[1], boost::is_any_of(";")); + + if (perfdata_values.size() == 0) { + Log(LogWarning, "icinga", "GraphiteWriter: Invalid performance data: '" + token + "'."); + return; + } + + /* TODO remove UOM from value */ + String metricValue = perfdata_values[0]; + + /* //TODO: Figure out how graphite handles warn/crit/min/max + String metricValueWarn, metricValueCrit, metricValueMin, metricValueMax; + + if (perfdata_values.size() > 1) + metricValueWarn = perfdata_values[1]; + if (perfdata_values.size() > 2) + metricValueCrit = perfdata_values[2]; + if (perfdata_values.size() > 3) + metricValueMin = perfdata_values[3]; + if (perfdata_values.size() > 4) + metricValueMax = perfdata_values[4]; + */ + + AddServiceMetric(metrics, service, metricName, metricValue); + } + } + + SendMetrics(metrics); +} + +void GraphiteWriter::AddServiceMetric(std::vector& metrics, const Service::Ptr& service, const String& name, const Value& value) +{ + /* TODO: sanitize host and service names */ + String hostName = service->GetHost()->GetName(); + String serviceName = service->GetShortName(); + String metricPrefix = hostName + "." + serviceName; + String graphitePrefix = "icinga"; + + String metric = graphitePrefix + ".service." + metricPrefix + "." + name + " " + Convert::ToString(value) + " " + Convert::ToString(static_cast(Utility::GetTime())) + "\n"; + Log(LogDebug, "icinga", "GraphiteWriter: Add to metric list:'" + metric + "'."); + metrics.push_back(metric); +} + +void GraphiteWriter::SendMetrics(const std::vector& metrics) +{ + if (!m_Stream) { + Log(LogWarning, "icinga", "GraphiteWriter not connected!"); + return; + } + + BOOST_FOREACH(const String& metric, metrics) { + if (metric.IsEmpty()) + continue; + + Log(LogDebug, "icinga", "GraphiteWriter: Sending metric '" + metric + "'."); + m_Stream->Write(metric.CStr(), metric.GetLength()); + } +} + +void GraphiteWriter::InternalSerialize(const Dictionary::Ptr& bag, int attributeTypes) const +{ + DynamicObject::InternalSerialize(bag, attributeTypes); + + if (attributeTypes & Attribute_Config) { + bag->Set("host", m_Host); + bag->Set("port", m_Port); + } +} + +void GraphiteWriter::InternalDeserialize(const Dictionary::Ptr& bag, int attributeTypes) +{ + DynamicObject::InternalDeserialize(bag, attributeTypes); + + if (attributeTypes & Attribute_Config) { + m_Host = bag->Get("host"); + m_Port = bag->Get("port"); + } +} diff --git a/components/perfdata/graphitewriter.h b/components/perfdata/graphitewriter.h new file mode 100644 index 000000000..0cba76352 --- /dev/null +++ b/components/perfdata/graphitewriter.h @@ -0,0 +1,68 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2013 Icinga Development Team (http://www.icinga.org/) * + * * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +#ifndef GRAPHITEWRITER_H +#define GRAPHITEWRITER_H + +#include "icinga/service.h" +#include "base/dynamicobject.h" +#include "base/tcpsocket.h" +#include "base/timer.h" +#include + +namespace icinga +{ + +/** + * An Icinga graphite writer. + * + * @ingroup perfdata + */ +class GraphiteWriter : public DynamicObject +{ +public: + DECLARE_PTR_TYPEDEFS(GraphiteWriter); + DECLARE_TYPENAME(GraphiteWriter); + + String GetHost(void) const; + String GetPort(void) const; + +protected: + virtual void Start(void); + + virtual void InternalSerialize(const Dictionary::Ptr& bag, int attributeTypes) const; + virtual void InternalDeserialize(const Dictionary::Ptr& bag, int attributeTypes); + +private: + String m_Host; + String m_Port; + Stream::Ptr m_Stream; + + Timer::Ptr m_ReconnectTimer; + + void CheckResultHandler(const Service::Ptr& service, const Dictionary::Ptr& cr); + static void AddServiceMetric(std::vector & metrics, const Service::Ptr& service, const String& name, const Value& value); + void SendMetrics(const std::vector& metrics); + + void ReconnectTimerHandler(void); +}; + +} + +#endif /* GRAPHITEWRITER_H */ diff --git a/components/perfdata/perfdata-type.conf b/components/perfdata/perfdata-type.conf new file mode 100644 index 000000000..de3a2abe8 --- /dev/null +++ b/components/perfdata/perfdata-type.conf @@ -0,0 +1,29 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2013 Icinga Development Team (http://www.icinga.org/) * + * * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ******************************************************************************/ + +type PerfdataWriter { + %attribute string "perfdata_path", + %attribute string "format_template", + %attribute number "rotation_interval" +} + +type GraphiteWriter { + %attribute string "host", + %attribute string "port", +} diff --git a/components/perfdata/perfdata.vcxproj b/components/perfdata/perfdata.vcxproj new file mode 100644 index 000000000..5f5cf24ac --- /dev/null +++ b/components/perfdata/perfdata.vcxproj @@ -0,0 +1,224 @@ + + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + NotUsing + NotUsing + NotUsing + NotUsing + + + + + + + + Document + "$(SolutionDir)$(Platform)\$(Configuration)\mkembedconfig.exe" "%(Identity)" "%(Filename).cpp" + Preparing config fragment for embedding + "$(SolutionDir)$(Platform)\$(Configuration)\mkembedconfig.exe" "%(Identity)" "%(Filename).cpp" + Preparing config fragment for embedding + "$(SolutionDir)$(Platform)\$(Configuration)\mkembedconfig.exe" "%(Identity)" "%(Filename).cpp" + Preparing config fragment for embedding + "$(SolutionDir)$(Platform)\$(Configuration)\mkembedconfig.exe" "%(Identity)" "%(Filename).cpp" + Preparing config fragment for embedding + %(Filename).cpp;%(Outputs) + %(Filename).cpp;%(Outputs) + %(Filename).cpp;%(Outputs) + %(Filename).cpp;%(Outputs) + $(SolutionDir)\tools\mkembedconfig\mkembedconfig.c + $(SolutionDir)\tools\mkembedconfig\mkembedconfig.c + $(SolutionDir)\tools\mkembedconfig\mkembedconfig.c + $(SolutionDir)\tools\mkembedconfig\mkembedconfig.c + + + + + + + {C1FC77E1-04A4-481B-A78B-2F7AF489C2F8} + Win32Proj + icinga + icinga + + + + DynamicLibrary + true + MultiByte + v110 + + + DynamicLibrary + true + MultiByte + v110 + + + DynamicLibrary + false + true + MultiByte + v110 + + + DynamicLibrary + false + true + MultiByte + v110 + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\lib;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\lib;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\lib;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\lib;$(IncludePath) + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + $(SolutionDir)$(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + + NotUsing + Disabled + _DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;I2_ICINGA_BUILD;%(PreprocessorDefinitions) + Level3 + false + i2-icinga.h + true + + + Console + true + base.lib;config.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + Disabled + _DEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;I2_ICINGA_BUILD;%(PreprocessorDefinitions) + Level3 + false + i2-icinga.h + true + + + Console + true + base.lib;config.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;I2_ICINGA_BUILD;%(PreprocessorDefinitions) + Speed + Level3 + false + i2-icinga.h + true + + + Console + true + true + true + base.lib;config.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + NotUsing + MaxSpeed + true + true + NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_SCL_SECURE_NO_WARNINGS;I2_ICINGA_BUILD;%(PreprocessorDefinitions) + Speed + Level3 + false + i2-icinga.h + true + + + Console + true + true + true + base.lib;config.lib;ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + diff --git a/components/perfdata/perfdata.vcxproj.filters b/components/perfdata/perfdata.vcxproj.filters new file mode 100644 index 000000000..5fc326f58 --- /dev/null +++ b/components/perfdata/perfdata.vcxproj.filters @@ -0,0 +1,29 @@ + + + + + Quelldateien + + + + + Headerdateien + + + + + {2c1a73da-f333-42fc-8809-0569990567e0} + + + {f206b9b0-3aa5-4a62-b844-3228f4ff4baf} + + + + + Quelldateien + + + + + + diff --git a/lib/icinga/perfdatawriter.cpp b/components/perfdata/perfdatawriter.cpp similarity index 99% rename from lib/icinga/perfdatawriter.cpp rename to components/perfdata/perfdatawriter.cpp index 4a9b4f5c0..c67b144c3 100644 --- a/lib/icinga/perfdatawriter.cpp +++ b/components/perfdata/perfdatawriter.cpp @@ -17,7 +17,7 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************/ -#include "icinga/perfdatawriter.h" +#include "perfdata/perfdatawriter.h" #include "icinga/service.h" #include "icinga/macroprocessor.h" #include "icinga/icingaapplication.h" diff --git a/lib/icinga/perfdatawriter.h b/components/perfdata/perfdatawriter.h similarity index 100% rename from lib/icinga/perfdatawriter.h rename to components/perfdata/perfdatawriter.h diff --git a/configure.ac b/configure.ac index 0c01ab3b0..ccc21c58e 100644 --- a/configure.ac +++ b/configure.ac @@ -180,6 +180,7 @@ components/db_ido_mysql/schema/Makefile components/db_ido_mysql/schema/upgrade/Makefile components/livestatus/Makefile components/notification/Makefile +components/perfdata/Makefile doc/Doxyfile doc/Makefile doc/strapdownjs/Makefile diff --git a/doc/4.3-object-types.md b/doc/4.3-object-types.md index 76a18e825..659cf9c67 100644 --- a/doc/4.3-object-types.md +++ b/doc/4.3-object-types.md @@ -374,7 +374,9 @@ Attributes: Writes check result performance data to a defined path using macro pattern. -Example +Example: + + library "perfdata" object PerfdataWriter "pnp" { perfdata_path = "/var/spool/icinga2/perfdata/service-perfdata", @@ -397,6 +399,27 @@ Attributes: > When rotating the performance data file the current UNIX timestamp is appended to the path specified > in `perfdata\_path` to generate a unique filename. +### GraphiteWriter + +Writes check result metrics and performance data to a defined +Graphite Carbon host. + +Example: + + library "perfdata" + + object GraphiteWriter "graphite" { + host = "127.0.0.1", + port = 2003 + } + +Attributes: + + Name |Description + ----------------|---------------- + host |**Optional.** Graphite Carbon host address. Defaults to '127.0.0.1'. + port |**Optional.** Graphite Carbon port. Defaults to 2003. + ### IdoMySqlConnection IDO DB schema compatible output into MySQL database. diff --git a/etc/icinga2/features-available/Makefile.am b/etc/icinga2/features-available/Makefile.am index 7963f7ecb..fc37d95f5 100644 --- a/etc/icinga2/features-available/Makefile.am +++ b/etc/icinga2/features-available/Makefile.am @@ -6,6 +6,7 @@ CONFIG_FILES = \ checker.conf \ command.conf \ compat-log.conf \ + graphite.conf \ ido-mysql.conf \ livestatus.conf \ notification.conf \ diff --git a/etc/icinga2/features-available/graphite.conf b/etc/icinga2/features-available/graphite.conf new file mode 100644 index 000000000..b1698b944 --- /dev/null +++ b/etc/icinga2/features-available/graphite.conf @@ -0,0 +1,11 @@ +/** + * The GraphiteWriter type writes check result metrics and + * performance data to a graphite tcp socket. + */ + +library "perfdata" + +object GraphiteWriter "graphite" { +// host = "127.0.0.1", +// port = 2003 +} diff --git a/etc/icinga2/features-available/perfdata.conf b/etc/icinga2/features-available/perfdata.conf index 5b959721e..4a518bfc2 100644 --- a/etc/icinga2/features-available/perfdata.conf +++ b/etc/icinga2/features-available/perfdata.conf @@ -3,4 +3,6 @@ * the in a regular interval. */ +library "perfdata" + object PerfdataWriter "perfdata" { } diff --git a/icinga-app/Makefile.am b/icinga-app/Makefile.am index a4996f874..9a4ef0ca7 100644 --- a/icinga-app/Makefile.am +++ b/icinga-app/Makefile.am @@ -31,7 +31,8 @@ icinga2_LDADD = \ -dlopen ${top_builddir}/components/compat/libcompat.la \ -dlopen ${top_builddir}/components/demo/libdemo.la \ -dlopen ${top_builddir}/components/livestatus/liblivestatus.la \ - -dlopen ${top_builddir}/components/notification/libnotification.la + -dlopen ${top_builddir}/components/notification/libnotification.la \ + -dlopen ${top_builddir}/components/perfdata/libperfdata.la if PYTHON_USE icinga2_LDADD += \ diff --git a/lib/db_ido/dbconnection.h b/lib/db_ido/dbconnection.h index bb546cf4f..aa0a67785 100644 --- a/lib/db_ido/dbconnection.h +++ b/lib/db_ido/dbconnection.h @@ -39,7 +39,7 @@ enum CleanUpAge /** * A database connection. * - * @ingroup ido + * @ingroup db_ido */ class DbConnection : public DynamicObject { diff --git a/lib/icinga/Makefile.am b/lib/icinga/Makefile.am index 88654d862..839581372 100644 --- a/lib/icinga/Makefile.am +++ b/lib/icinga/Makefile.am @@ -49,8 +49,6 @@ libicinga_la_SOURCES = \ nullchecktask.h \ nulleventtask.cpp \ nulleventtask.h \ - perfdatawriter.cpp \ - perfdatawriter.h \ pluginchecktask.cpp \ pluginchecktask.h \ plugineventtask.cpp \ diff --git a/lib/icinga/icinga-type.conf b/lib/icinga/icinga-type.conf index d21221985..0a95ff167 100644 --- a/lib/icinga/icinga-type.conf +++ b/lib/icinga/icinga-type.conf @@ -182,12 +182,6 @@ type TimePeriod { /* } */ } -type PerfdataWriter { - %attribute string "perfdata_path", - %attribute string "format_template", - %attribute number "rotation_interval" -} - type Command { %require "methods", %attribute dictionary "methods" {