From e25fe1ecc9ba1d52c2441b17623a9e5766dc5e33 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 7 Nov 2013 11:11:44 +0100 Subject: [PATCH 1/6] Optimize ObjectImpl<>::GetDefault*(). Refs #2710 --- tools/mkclass/classcompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index 0d651af0b..4aff41c06 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -349,7 +349,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo& locp) << "\t" << "{" << std::endl; if (it->DefaultAccessor.empty()) - std::cout << "\t\t" << "return Empty;" << std::endl; + std::cout << "\t\t" << "return " << it->Type << "();" << std::endl; else std::cout << it->DefaultAccessor << std::endl; From 14553139aec0cc2bca9634a5295cbf8a70d4382a Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 7 Nov 2013 12:17:06 +0100 Subject: [PATCH 2/6] Implement String::FindFirstNotOf. Refs #2710 --- lib/base/qstring.cpp | 10 ++++++++++ lib/base/qstring.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/lib/base/qstring.cpp b/lib/base/qstring.cpp index 80e08261a..c36281207 100644 --- a/lib/base/qstring.cpp +++ b/lib/base/qstring.cpp @@ -142,6 +142,16 @@ size_t String::FindFirstOf(char ch, size_t pos) const return m_Data.find_first_of(ch, pos); } +size_t String::FindFirstNotOf(const char *s, size_t pos) const +{ + return m_Data.find_first_not_of(s, pos); +} + +size_t String::FindFirstNotOf(char ch, size_t pos) const +{ + return m_Data.find_first_not_of(ch, pos); +} + String String::SubStr(size_t first, size_t len) const { return m_Data.substr(first, len); diff --git a/lib/base/qstring.h b/lib/base/qstring.h index e26717fca..9ca0e5e5f 100644 --- a/lib/base/qstring.h +++ b/lib/base/qstring.h @@ -81,6 +81,8 @@ public: size_t Find(const String& str, size_t pos = 0) const; size_t FindFirstOf(const char *s, size_t pos = 0) const; size_t FindFirstOf(char ch, size_t pos = 0) const; + size_t FindFirstNotOf(const char *s, size_t pos = 0) const; + size_t FindFirstNotOf(char ch, size_t pos = 0) const; String SubStr(size_t first, size_t len = NPos) const; void Replace(size_t first, size_t second, const String& str); From e93d3f4c61f9d9f9e1ec4154d54d029f1ebfac54 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 7 Nov 2013 12:19:27 +0100 Subject: [PATCH 3/6] Update gdbinit path. Refs #2710 --- tools/debug/gdb/gdbinit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/debug/gdb/gdbinit b/tools/debug/gdb/gdbinit index afc9d3c18..92551e356 100644 --- a/tools/debug/gdb/gdbinit +++ b/tools/debug/gdb/gdbinit @@ -2,7 +2,7 @@ set env LD_PRELOAD /lib/x86_64-linux-gnu/libpthread.so.0 python import sys -sys.path.insert(0, '/home/gbeutner/strawberry/tools/debug/gdb') +sys.path.insert(0, '/home/gbeutner/icinga2/tools/debug/gdb') from icingadbg import register_icinga_printers register_icinga_printers() end From 038be974e4b3ff1b7ad31c43550fc4a5c584dbcc Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 7 Nov 2013 13:41:24 +0100 Subject: [PATCH 4/6] Implement additional operators for the Value and String classes. Refs #2710 --- lib/base/qstring.cpp | 7 ++++ lib/base/qstring.h | 3 ++ lib/base/value.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++++ lib/base/value.h | 18 ++++++++++ 4 files changed, 109 insertions(+) diff --git a/lib/base/qstring.cpp b/lib/base/qstring.cpp index c36281207..c3ca495ac 100644 --- a/lib/base/qstring.cpp +++ b/lib/base/qstring.cpp @@ -18,6 +18,7 @@ ******************************************************************************/ #include "base/qstring.h" +#include "base/value.h" #include #include #include @@ -86,6 +87,12 @@ String& String::operator+=(const char *rhs) return *this; } +String& String::operator+=(const Value& rhs) +{ + m_Data += static_cast(rhs); + return *this; +} + String& String::operator+=(char rhs) { m_Data += rhs; diff --git a/lib/base/qstring.h b/lib/base/qstring.h index 9ca0e5e5f..74f0202c0 100644 --- a/lib/base/qstring.h +++ b/lib/base/qstring.h @@ -28,6 +28,8 @@ namespace icinga { +class Value; + /** * String class. * @@ -64,6 +66,7 @@ public: String& operator+=(const String& rhs); String& operator+=(const char *rhs); + String& operator+=(const Value& rhs); String& operator+=(char rhs); bool IsEmpty(void) const; diff --git a/lib/base/value.cpp b/lib/base/value.cpp index 23668959d..0216bb88b 100644 --- a/lib/base/value.cpp +++ b/lib/base/value.cpp @@ -241,6 +241,77 @@ ValueType Value::GetType(void) const return static_cast(m_Value.which()); } +bool Value::operator==(int rhs) +{ + if (!IsScalar()) + return false; + + return static_cast(*this) == rhs; +} + +bool Value::operator!=(int rhs) +{ + return !(*this == rhs); +} + +bool Value::operator==(double rhs) +{ + if (!IsScalar()) + return false; + + return static_cast(*this) == rhs; +} + +bool Value::operator!=(double rhs) +{ + return !(*this == rhs); +} + +bool Value::operator==(const char *rhs) +{ + return static_cast(*this) == rhs; +} + +bool Value::operator!=(const char *rhs) +{ + return !(*this == rhs); +} + +bool Value::operator==(const String& rhs) +{ + return static_cast(*this) == rhs; +} + +bool Value::operator!=(const String& rhs) +{ + return !(*this == rhs); +} + +bool Value::operator==(const Value& rhs) +{ + if (IsEmpty() != rhs.IsEmpty()) + return false; + + if (IsEmpty()) + return true; + + if (IsObject() != rhs.IsObject()) + return false; + + if (IsObject()) + return static_cast(*this) == static_cast(rhs); + + if (GetType() == ValueNumber || rhs.GetType() == ValueNumber) + return static_cast(*this) == static_cast(rhs); + else + return static_cast(*this) == static_cast(rhs); +} + +bool Value::operator!=(const Value& rhs) +{ + return !(*this == rhs); +} + Value icinga::operator+(const Value& lhs, const char *rhs) { return static_cast(lhs) + rhs; @@ -251,6 +322,16 @@ Value icinga::operator+(const char *lhs, const Value& rhs) return lhs + static_cast(rhs); } +Value icinga::operator+(const Value& lhs, const String& rhs) +{ + return static_cast(lhs) + rhs; +} + +Value icinga::operator+(const String& lhs, const Value& rhs) +{ + return lhs + static_cast(rhs); +} + std::ostream& icinga::operator<<(std::ostream& stream, const Value& value) { stream << static_cast(value); diff --git a/lib/base/value.h b/lib/base/value.h index 1abf97936..29bf59b26 100644 --- a/lib/base/value.h +++ b/lib/base/value.h @@ -73,6 +73,21 @@ public: operator double(void) const; operator String(void) const; + bool operator==(int rhs); + bool operator!=(int rhs); + + bool operator==(double rhs); + bool operator!=(double rhs); + + bool operator==(const char *rhs); + bool operator!=(const char *rhs); + + bool operator==(const String& rhs); + bool operator!=(const String& rhs); + + bool operator==(const Value& rhs); + bool operator!=(const Value& rhs); + template operator shared_ptr(void) const { @@ -117,6 +132,9 @@ static Value Empty; I2_BASE_API Value operator+(const Value& lhs, const char *rhs); I2_BASE_API Value operator+(const char *lhs, const Value& rhs); +I2_BASE_API Value operator+(const Value& lhs, const String& rhs); +I2_BASE_API Value operator+(const String& lhs, const Value& rhs); + I2_BASE_API std::ostream& operator<<(std::ostream& stream, const Value& value); I2_BASE_API std::istream& operator>>(std::istream& stream, Value& value); From 2af388e4ef7e7b9868d1fa3eabadb032e0950c00 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 7 Nov 2013 13:37:58 +0100 Subject: [PATCH 5/6] Implement performance data parser. Refs #2710 --- lib/icinga/CMakeLists.txt | 11 ++-- lib/icinga/perfdatavalue.cpp | 105 +++++++++++++++++++++++++++++++++++ lib/icinga/perfdatavalue.h | 25 +++++++++ lib/icinga/perfdatavalue.ti | 17 ++++++ lib/icinga/pluginutility.cpp | 55 ++++++++++++++++++ lib/icinga/pluginutility.h | 3 + test/CMakeLists.txt | 13 ++++- test/icinga-perfdata.cpp | 90 ++++++++++++++++++++++++++++++ 8 files changed, 312 insertions(+), 7 deletions(-) create mode 100644 lib/icinga/perfdatavalue.cpp create mode 100644 lib/icinga/perfdatavalue.h create mode 100644 lib/icinga/perfdatavalue.ti create mode 100644 test/icinga-perfdata.cpp diff --git a/lib/icinga/CMakeLists.txt b/lib/icinga/CMakeLists.txt index 8f2cdf089..61f959c25 100644 --- a/lib/icinga/CMakeLists.txt +++ b/lib/icinga/CMakeLists.txt @@ -24,6 +24,7 @@ mkclass_target(host.ti host.th) mkclass_target(icingaapplication.ti icingaapplication.th) mkclass_target(notificationcommand.ti notificationcommand.th) mkclass_target(notification.ti notification.th) +mkclass_target(perfdatavalue.ti perfdatavalue.th) mkclass_target(servicegroup.ti servicegroup.th) mkclass_target(service.ti service.th) mkclass_target(timeperiod.ti timeperiod.th) @@ -38,11 +39,11 @@ add_library(icinga SHARED externalcommandprocessor.cpp host.cpp host.th hostgroup.cpp hostgroup.th icingaapplication.cpp icingaapplication.th macroprocessor.cpp macroresolver.cpp notificationcommand.cpp notificationcommand.th - notification.cpp notification.th pluginutility.cpp service-check.cpp - service-comment.cpp service.cpp service-downtime.cpp service-event.cpp - service-flapping.cpp service.th servicegroup.cpp servicegroup.th - service-notification.cpp timeperiod.cpp timeperiod.th user.cpp user.th - usergroup.cpp usergroup.th icinga-type.cpp + notification.cpp notification.th perfdatavalue.cpp perfdatavalue.th + pluginutility.cpp service-check.cpp service-comment.cpp service.cpp + service-downtime.cpp service-event.cpp service-flapping.cpp service.th + servicegroup.cpp servicegroup.th service-notification.cpp timeperiod.cpp + timeperiod.th user.cpp user.th usergroup.cpp usergroup.th icinga-type.cpp ) target_link_libraries(icinga ${Boost_LIBRARIES} base config) diff --git a/lib/icinga/perfdatavalue.cpp b/lib/icinga/perfdatavalue.cpp new file mode 100644 index 000000000..3769b072f --- /dev/null +++ b/lib/icinga/perfdatavalue.cpp @@ -0,0 +1,105 @@ +#include "icinga/perfdatavalue.h" +#include "base/convert.h" +#include +#include + +using namespace icinga; + +PerfdataValue::PerfdataValue(double value, bool counter, const String& unit, + const Value& warn, const Value& crit, const Value& min, const Value& max) +{ + SetValue(value); + SetCounter(counter); + SetUnit(unit); + SetWarn(warn); + SetCrit(crit); + SetMin(min); + SetMax(max); +} + +Value PerfdataValue::Parse(const String& perfdata) +{ + size_t pos = perfdata.FindFirstNotOf("+-0123456789.e"); + + double value = Convert::ToDouble(perfdata.SubStr(0, pos)); + + if (pos == String::NPos) + return value; + + std::vector tokens; + boost::algorithm::split(tokens, perfdata, boost::is_any_of(";")); + + bool counter = false; + String unit; + Value warn, crit, min, max; + + unit = perfdata.SubStr(pos, tokens[0].GetLength() - pos); + + if (unit == "s") + unit = "seconds"; + else if (unit == "b") + unit = "bytes"; + else if (unit == "%") + unit = "percent"; + else if (unit == "c") { + counter = true; + unit = ""; + } else + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid performance data unit: " + unit)); + + if (tokens.size() > 1 && tokens[1] != "U") + warn = Convert::ToDouble(tokens[1]); + + if (tokens.size() > 2 && tokens[2] != "U") + crit = Convert::ToDouble(tokens[2]); + + if (tokens.size() > 3 && tokens[3] != "U") + min = Convert::ToDouble(tokens[3]); + + if (tokens.size() > 4 && tokens[4] != "U") + max = Convert::ToDouble(tokens[4]); + + return make_shared(value, counter, unit, warn, crit, min, max); +} + +String PerfdataValue::Format(const Value& perfdata) +{ + if (perfdata.IsObjectType()) { + PerfdataValue::Ptr pdv = perfdata; + + String output = Convert::ToString(pdv->GetValue()); + + String unit; + + if (pdv->GetCounter()) + unit = "c"; + else if (pdv->GetUnit() == "seconds") + unit = "s"; + else if (pdv->GetUnit() == "percent") + unit = "%"; + else if (pdv->GetUnit() == "bytes") + unit = "b"; + + output += unit; + + if (!pdv->GetWarn().IsEmpty()) { + output += ";" + pdv->GetWarn(); + + if (!pdv->GetCrit().IsEmpty()) { + output += ";" + pdv->GetCrit(); + + if (!pdv->GetMin().IsEmpty()) { + output += ";" + pdv->GetMin(); + + if (!pdv->GetMax().IsEmpty()) { + output += ";" + pdv->GetMax(); + } + } + } + } + + return output; + } else { + return perfdata; + } +} diff --git a/lib/icinga/perfdatavalue.h b/lib/icinga/perfdatavalue.h new file mode 100644 index 000000000..4ec518db9 --- /dev/null +++ b/lib/icinga/perfdatavalue.h @@ -0,0 +1,25 @@ +#ifndef PERFDATAVALUE_H +#define PERFDATAVALUE_H + +#include "icinga/i2-icinga.h" +#include "icinga/perfdatavalue.th" + +namespace icinga +{ + +class I2_ICINGA_API PerfdataValue : public ObjectImpl +{ +public: + DECLARE_PTR_TYPEDEFS(PerfdataValue); + + PerfdataValue(double value, bool counter, const String& unit, + const Value& warn = Empty, const Value& crit = Empty, + const Value& min = Empty, const Value& max = Empty); + + static Value Parse(const String& perfdata); + static String Format(const Value& perfdata); +}; + +} + +#endif /* PERFDATA_VALUE */ diff --git a/lib/icinga/perfdatavalue.ti b/lib/icinga/perfdatavalue.ti new file mode 100644 index 000000000..b2afa1072 --- /dev/null +++ b/lib/icinga/perfdatavalue.ti @@ -0,0 +1,17 @@ +#include "base/dynamicobject.h" + +namespace icinga +{ + +class PerfdataValue +{ + [state] double Value; + [state] bool Counter; + [state] String Unit; + [state] Value Crit; + [state] Value Warn; + [state] Value Min; + [state] Value Max; +}; + +} diff --git a/lib/icinga/pluginutility.cpp b/lib/icinga/pluginutility.cpp index b7696dbe2..5589870e5 100644 --- a/lib/icinga/pluginutility.cpp +++ b/lib/icinga/pluginutility.cpp @@ -21,6 +21,7 @@ #include "icinga/checkcommand.h" #include "icinga/macroprocessor.h" #include "icinga/icingaapplication.h" +#include "icinga/perfdatavalue.h" #include "base/dynamictype.h" #include "base/logger_fwd.h" #include "base/scriptfunction.h" @@ -79,3 +80,57 @@ Dictionary::Ptr PluginUtility::ParseCheckOutput(const String& output) return result; } + +Value PluginUtility::ParsePerfdata(const String& perfdata) +{ + try { + Dictionary::Ptr result = make_shared(); + + size_t begin = 0; + + for (;;) { + size_t eqp = perfdata.FindFirstOf('=', begin); + + if (eqp == String::NPos) + break; + + String key = perfdata.SubStr(begin, eqp - begin); + + size_t spq = perfdata.FindFirstOf(' ', eqp); + + if (spq == String::NPos) + spq = perfdata.GetLength(); + + String value = perfdata.SubStr(eqp + 1, spq - eqp - 1); + + result->Set(key, PerfdataValue::Parse(value)); + + begin = spq + 1; + } + + return result; + } catch (const std::exception&) { + return perfdata; + } +} + +String PluginUtility::FormatPerfdata(const Value& perfdata) +{ + String output; + + if (!perfdata.IsObjectType()) + return perfdata; + + Dictionary::Ptr dict = perfdata; + + String key; + Value value; + BOOST_FOREACH(boost::tie(key, value), dict) { + if (!output.IsEmpty()) + output += " "; + + output += key + "=" + PerfdataValue::Format(value); + } + + return output; +} diff --git a/lib/icinga/pluginutility.h b/lib/icinga/pluginutility.h index 96cca7897..5c01ceaaa 100644 --- a/lib/icinga/pluginutility.h +++ b/lib/icinga/pluginutility.h @@ -41,6 +41,9 @@ public: static ServiceState ExitStatusToState(int exitStatus); static Dictionary::Ptr ParseCheckOutput(const String& output); + static Value ParsePerfdata(const String& perfdata); + static String FormatPerfdata(const Value& perfdata); + private: PluginUtility(void); }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 56831cd96..a166ea369 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,8 +18,11 @@ include(BoostTestTargets) add_boost_test(base - SOURCES base-array.cpp base-convert.cpp base-dictionary.cpp base-fifo.cpp base-match.cpp base-netstring.cpp base-object.cpp base-shellescape.cpp base-stacktrace.cpp base-string.cpp base-timer.cpp base-value.cpp test.cpp - LIBRARIES base + SOURCES base-array.cpp base-convert.cpp base-dictionary.cpp base-fifo.cpp + base-match.cpp base-netstring.cpp base-object.cpp base-shellescape.cpp + base-stacktrace.cpp base-string.cpp base-timer.cpp base-value.cpp + icinga-perfdata.cpp test.cpp + LIBRARIES base config icinga TESTS base_array/construct base_array/getset base_array/remove @@ -62,5 +65,11 @@ add_boost_test(base base_value/scalar base_value/convert base_value/format + icinga_perfdata/simple + icinga_perfdata/multiple + icinga_perfdata/uom + icinga_perfdata/warncrit + icinga_perfdata/minmax + icinga_perfdata/invalid ) diff --git a/test/icinga-perfdata.cpp b/test/icinga-perfdata.cpp new file mode 100644 index 000000000..be39783c4 --- /dev/null +++ b/test/icinga-perfdata.cpp @@ -0,0 +1,90 @@ +/****************************************************************************** + * 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 "icinga/pluginutility.cpp" +#include + +using namespace icinga; + +BOOST_AUTO_TEST_SUITE(icinga_perfdata) + +BOOST_AUTO_TEST_CASE(simple) +{ + Dictionary::Ptr pd = PluginUtility::ParsePerfdata("test=123456"); + BOOST_CHECK(pd->Get("test") == 123456); + + String str = PluginUtility::FormatPerfdata(pd); + BOOST_CHECK(str == "test=123456"); +} + +BOOST_AUTO_TEST_CASE(multiple) +{ + Dictionary::Ptr pd = PluginUtility::ParsePerfdata("testA=123456 testB=123456"); + BOOST_CHECK(pd->Get("testA") == 123456); + BOOST_CHECK(pd->Get("testB") == 123456); + + String str = PluginUtility::FormatPerfdata(pd); + BOOST_CHECK(str == "testA=123456 testB=123456"); +} + +BOOST_AUTO_TEST_CASE(uom) +{ + Dictionary::Ptr pd = PluginUtility::ParsePerfdata("test=123456b"); + + PerfdataValue::Ptr pv = pd->Get("test"); + BOOST_CHECK(pv); + + BOOST_CHECK(pv->GetValue() == 123456); + BOOST_CHECK(!pv->GetCounter()); + BOOST_CHECK(pv->GetUnit() == "bytes"); + BOOST_CHECK(pv->GetCrit() == Empty); + BOOST_CHECK(pv->GetWarn() == Empty); + BOOST_CHECK(pv->GetMin() == Empty); + BOOST_CHECK(pv->GetMax() == Empty); + + String str = PluginUtility::FormatPerfdata(pd); + BOOST_CHECK(str == "test=123456b"); +} + +BOOST_AUTO_TEST_CASE(warncrit) +{ + Dictionary::Ptr pd = PluginUtility::ParsePerfdata("test=123456b;1000;2000"); + + PerfdataValue::Ptr pv = pd->Get("test"); + BOOST_CHECK(pv); + + BOOST_CHECK(pv->GetValue() == 123456); + BOOST_CHECK(!pv->GetCounter()); + BOOST_CHECK(pv->GetUnit() == "bytes"); + BOOST_CHECK(pv->GetWarn() == 1000); + BOOST_CHECK(pv->GetCrit() == 2000); + BOOST_CHECK(pv->GetMin() == Empty); + BOOST_CHECK(pv->GetMax() == Empty); + + String str = PluginUtility::FormatPerfdata(pd); + BOOST_CHECK(str == "test=123456b;1000;2000"); +} + +BOOST_AUTO_TEST_CASE(invalid) +{ + BOOST_CHECK(PluginUtility::ParsePerfdata("test=1,23456") == "test=1,23456"); + BOOST_CHECK(PluginUtility::ParsePerfdata("test=123456;10%;20%") == "test=123456;10%;20%"); +} + +BOOST_AUTO_TEST_SUITE_END() From 5aba175e18e0f836238e2932eeaf29db827fe607 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 7 Nov 2013 14:38:37 +0100 Subject: [PATCH 6/6] Use PluginUtility::{Parse,Format}Perfdata for check results. Refs #2710 --- components/perfdata/graphitewriter.cpp | 163 +++++-------------------- components/perfdata/graphitewriter.h | 5 +- lib/icinga/compatutility.cpp | 11 +- lib/icinga/host.cpp | 3 +- lib/icinga/pluginutility.cpp | 2 +- lib/icinga/service.cpp | 3 +- lib/methods/nullchecktask.cpp | 6 +- lib/methods/randomchecktask.cpp | 6 +- 8 files changed, 53 insertions(+), 146 deletions(-) diff --git a/components/perfdata/graphitewriter.cpp b/components/perfdata/graphitewriter.cpp index f5284888d..4a101223f 100644 --- a/components/perfdata/graphitewriter.cpp +++ b/components/perfdata/graphitewriter.cpp @@ -22,6 +22,7 @@ #include "icinga/macroprocessor.h" #include "icinga/icingaapplication.h" #include "icinga/compatutility.h" +#include "icinga/perfdatavalue.h" #include "base/tcpsocket.h" #include "base/dynamictype.h" #include "base/objectlock.h" @@ -82,122 +83,36 @@ void GraphiteWriter::CheckResultHandler(const Service::Ptr& service, const Dicti if (!IcingaApplication::GetInstance()->GetEnablePerfdata() || !service->GetEnablePerfdata()) return; - Host::Ptr host = service->GetHost(); + /* basic metrics */ + SendMetric(service, "current_attempt", service->GetCheckAttempt()); + SendMetric(service, "max_check_attempts", service->GetMaxCheckAttempts()); + SendMetric(service, "state_type", service->GetStateType()); + SendMetric(service, "state", service->GetState()); + SendMetric(service, "latency", Service::CalculateLatency(cr)); + SendMetric(service, "execution_time", Service::CalculateExecutionTime(cr)); - if (!host) + Value pdv = cr->Get("performance_data"); + + if (!pdv.IsObjectType()) return; - /* service metrics */ - std::vector metrics; - String metricName; - Value metricValue; + Dictionary::Ptr perfdata = pdv; - /* basic metrics */ - AddServiceMetric(metrics, service, "current_attempt", service->GetCheckAttempt()); - 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)); + String key; + Value value; + BOOST_FOREACH(boost::tie(key, value), perfdata) { + double valueNum; - /* performance data metrics */ - String perfdata = CompatUtility::GetCheckResultPerfdata(cr); + if (!value.IsObjectType()) + valueNum = value; + else + valueNum = static_cast(value)->GetValue(); - if (!perfdata.IsEmpty()) { - perfdata.Trim(); - - Log(LogDebug, "perfdata", "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 white spaces in single quoted labels: 'foo bar'=0;;; 'baz'=1.0;;; - * 1. find first ', find second ' -> if no '=' in between, this is a label - * 2. two single quotes define an escaped single quite - * 3. warn/crit/min/max may be null and semicolon delimiter omitted - * https://www.nagios-plugins.org/doc/guidelines.html#AEN200 - */ - BOOST_FOREACH(const String& token, tokens) { - String metricKeyVal = token; - metricKeyVal.Trim(); - - std::vector key_val; - boost::algorithm::split(key_val, metricKeyVal, boost::is_any_of("=")); - - if (key_val.size() == 0) { - Log(LogWarning, "perfdata", "GraphiteWriter: Invalid performance data. No assignment operator found in :'" + metricKeyVal + "'."); - return; - } - - String metricName = key_val[0]; - metricName.Trim(); - - if (key_val.size() == 1) { - Log(LogWarning, "perfdata", "GraphiteWriter: Invalid performance data: '" + metricKeyVal + "' with key: '" + metricName + "'."); - return; - } - - String metricValues = key_val[1]; - metricValues.Trim(); - - std::vector perfdata_values; - boost::algorithm::split(perfdata_values, metricValues, boost::is_any_of(";")); - - if (perfdata_values.size() == 0) { - Log(LogWarning, "perfdata", "GraphiteWriter: Invalid performance data: '" + metricKeyVal + - "' with key: '" + metricName + "' and values: '" + metricValues + "'."); - return; - } - - String metricValue = perfdata_values[0]; - - metricValue.Trim(); - Log(LogDebug, "perfdata", "GraphiteWriter: Trimmed metric value: '" + metricValue + "'."); - - /* extract raw value (digit number digit as double) and uom - * http://en.highscore.de/cpp/boost/stringhandling.html - */ - String metricValueRaw = boost::algorithm::trim_right_copy_if(metricValue, (!boost::algorithm::is_digit() && !boost::algorithm::is_any_of(".,"))); - String metricValueUom = boost::algorithm::trim_left_copy_if(metricValue, (boost::algorithm::is_digit() || boost::algorithm::is_any_of(".,"))); - - Log(LogDebug, "perfdata", "GraphiteWriter: Raw metric value: '" + metricValueRaw + "' with UOM: '" + metricValueUom + "'."); - - /* TODO: Normalize raw value based on UOM - * a. empty - assume a number - * b. 's' - seconds (us, ms) - * c. '%' - percentage - * d. 'B' - bytes (KB, MB, GB, TB) - * e. 'c' - continous counter (snmp) - */ - - /* //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]; - */ - - /* sanitize invalid metric characters */ - SanitizeMetric(metricName); - - AddServiceMetric(metrics, service, metricName, metricValueRaw); - } + SendMetric(service, key, valueNum); } - - SendMetrics(metrics); } -void GraphiteWriter::AddServiceMetric(std::vector& metrics, const Service::Ptr& service, const String& name, const Value& value) +void GraphiteWriter::SendMetric(const Service::Ptr& service, const String& name, double value) { /* TODO: sanitize host and service names */ String hostName = service->GetHost()->GetName(); @@ -211,33 +126,22 @@ void GraphiteWriter::AddServiceMetric(std::vector& metrics, const Servic String metric = graphitePrefix + "." + metricPrefix + "." + name + " " + Convert::ToString(value) + " " + Convert::ToString(static_cast(Utility::GetTime())) + "\n"; Log(LogDebug, "perfdata", "GraphiteWriter: Add to metric list:'" + metric + "'."); - metrics.push_back(metric); -} -void GraphiteWriter::SendMetrics(const std::vector& metrics) -{ - BOOST_FOREACH(const String& metric, metrics) { - if (metric.IsEmpty()) - continue; + ObjectLock olock(this); - Log(LogDebug, "perfdata", "GraphiteWriter: Sending metric '" + metric + "'."); + if (!m_Stream) + return; - ObjectLock olock(this); + try { + m_Stream->Write(metric.CStr(), metric.GetLength()); + } catch (const std::exception& ex) { + std::ostringstream msgbuf; + msgbuf << "Exception thrown while writing to the Graphite socket: " << std::endl + << boost::diagnostic_information(ex); - if (!m_Stream) - return; + Log(LogCritical, "base", msgbuf.str()); - try { - m_Stream->Write(metric.CStr(), metric.GetLength()); - } catch (const std::exception& ex) { - std::ostringstream msgbuf; - msgbuf << "Exception thrown while writing to the Graphite socket: " << std::endl - << boost::diagnostic_information(ex); - - Log(LogCritical, "base", msgbuf.str()); - - m_Stream.reset(); - } + m_Stream.reset(); } } @@ -249,4 +153,3 @@ void GraphiteWriter::SanitizeMetric(String& str) boost::replace_all(str, "\\", "_"); boost::replace_all(str, "/", "_"); } - diff --git a/components/perfdata/graphitewriter.h b/components/perfdata/graphitewriter.h index b99736338..133790fd1 100644 --- a/components/perfdata/graphitewriter.h +++ b/components/perfdata/graphitewriter.h @@ -50,10 +50,9 @@ private: 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 SendMetric(const Service::Ptr& service, const String& name, double value); static void SanitizeMetric(String& str); - + void ReconnectTimerHandler(void); }; diff --git a/lib/icinga/compatutility.cpp b/lib/icinga/compatutility.cpp index 511701756..de26f8c91 100644 --- a/lib/icinga/compatutility.cpp +++ b/lib/icinga/compatutility.cpp @@ -17,13 +17,14 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ******************************************************************************/ -#include "base/convert.h" #include "icinga/compatutility.h" #include "icinga/checkcommand.h" #include "icinga/eventcommand.h" +#include "icinga/pluginutility.h" #include "base/dynamictype.h" #include "base/objectlock.h" #include "base/debug.h" +#include "base/convert.h" #include #include #include @@ -556,12 +557,10 @@ Dictionary::Ptr CompatUtility::GetCheckResultOutput(const Dictionary::Ptr& cr) String CompatUtility::GetCheckResultPerfdata(const Dictionary::Ptr& cr) { if (!cr) - return Empty; + return String(); - String perfdata = EscapeString(cr->Get("performance_data_raw")); - perfdata.Trim(); - - return perfdata; + Dictionary::Ptr perfdata = cr->Get("performance_data"); + return PluginUtility::FormatPerfdata(perfdata); } String CompatUtility::EscapeString(const String& str) diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 13e6d82cf..7dbc04cfe 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -21,6 +21,7 @@ #include "icinga/service.h" #include "icinga/hostgroup.h" #include "icinga/icingaapplication.h" +#include "icinga/pluginutility.h" #include "base/dynamictype.h" #include "base/objectlock.h" #include "base/logger_fwd.h" @@ -619,7 +620,7 @@ bool Host::ResolveMacro(const String& macro, const Dictionary::Ptr&, String *res *result = hccr->Get("output"); return true; } else if (macro == "HOSTPERFDATA") { - *result = hccr->Get("performance_data_raw"); + *result = PluginUtility::FormatPerfdata(hccr->Get("performance_data")); return true; } else if (macro == "LASTHOSTCHECK") { *result = Convert::ToString((long)hccr->Get("schedule_start")); diff --git a/lib/icinga/pluginutility.cpp b/lib/icinga/pluginutility.cpp index 5589870e5..4e9af85e9 100644 --- a/lib/icinga/pluginutility.cpp +++ b/lib/icinga/pluginutility.cpp @@ -76,7 +76,7 @@ Dictionary::Ptr PluginUtility::ParseCheckOutput(const String& output) } result->Set("output", text); - result->Set("performance_data_raw", perfdata); + result->Set("performance_data", ParsePerfdata(perfdata)); return result; } diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index 38d9848f9..484c9abda 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -22,6 +22,7 @@ #include "icinga/checkcommand.h" #include "icinga/icingaapplication.h" #include "icinga/macroprocessor.h" +#include "icinga/pluginutility.h" #include "config/configitembuilder.h" #include "base/dynamictype.h" #include "base/objectlock.h" @@ -393,7 +394,7 @@ bool Service::ResolveMacro(const String& macro, const Dictionary::Ptr& cr, Strin *result = cr->Get("output"); return true; } else if (macro == "SERVICEPERFDATA") { - *result = cr->Get("performance_data_raw"); + *result = PluginUtility::FormatPerfdata(cr->Get("performance_data")); return true; } else if (macro == "LASTSERVICECHECK") { *result = Convert::ToString((long)cr->Get("execution_end")); diff --git a/lib/methods/nullchecktask.cpp b/lib/methods/nullchecktask.cpp index 42368c572..01e80fdca 100644 --- a/lib/methods/nullchecktask.cpp +++ b/lib/methods/nullchecktask.cpp @@ -39,11 +39,13 @@ Dictionary::Ptr NullCheckTask::ScriptFunc(const Service::Ptr&) String output = "Hello from "; output += name; - String perfdata = "time=" + Convert::ToString(static_cast(Utility::GetTime())); + + Dictionary::Ptr perfdata = make_shared(); + perfdata->Set("time", Utility::GetTime()); Dictionary::Ptr cr = make_shared(); cr->Set("output", output); - cr->Set("performance_data_raw", perfdata); + cr->Set("performance_data", perfdata); cr->Set("state", StateOK); return cr; diff --git a/lib/methods/randomchecktask.cpp b/lib/methods/randomchecktask.cpp index 9c6f9b8fe..2ca8c4b70 100644 --- a/lib/methods/randomchecktask.cpp +++ b/lib/methods/randomchecktask.cpp @@ -39,11 +39,13 @@ Dictionary::Ptr RandomCheckTask::ScriptFunc(const Service::Ptr&) String output = "Hello from "; output += name; - String perfdata = "time=" + Convert::ToString(static_cast(Utility::GetTime())); + + Dictionary::Ptr perfdata = make_shared(); + perfdata->Set("time", Utility::GetTime()); Dictionary::Ptr cr = make_shared(); cr->Set("output", output); - cr->Set("performance_data_raw", perfdata); + cr->Set("performance_data", perfdata); cr->Set("state", static_cast(Utility::Random() % 4)); return cr;