From a18fbcb27a6a8e794565daabd310a8a561801473 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Mon, 27 Nov 2017 16:06:59 +0100 Subject: [PATCH 01/35] Fix missing variable name which can lead to segfaults refs #5808 refs #5807 Signed-off-by: Michael Friedrich --- lib/remote/apilistener.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 86cc79759..b00c37f6a 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -1354,37 +1354,37 @@ double ApiListener::CalculateZoneLag(const Endpoint::Ptr& endpoint) void ApiListener::AddAnonymousClient(const JsonRpcConnection::Ptr& aclient) { - boost::mutex::scoped_lock(m_AnonymousClientsLock); + boost::mutex::scoped_lock lock(m_AnonymousClientsLock); m_AnonymousClients.insert(aclient); } void ApiListener::RemoveAnonymousClient(const JsonRpcConnection::Ptr& aclient) { - boost::mutex::scoped_lock(m_AnonymousClientsLock); + boost::mutex::scoped_lock lock(m_AnonymousClientsLock); m_AnonymousClients.erase(aclient); } std::set ApiListener::GetAnonymousClients(void) const { - boost::mutex::scoped_lock(m_AnonymousClientsLock); + boost::mutex::scoped_lock lock(m_AnonymousClientsLock); return m_AnonymousClients; } void ApiListener::AddHttpClient(const HttpServerConnection::Ptr& aclient) { - boost::mutex::scoped_lock(m_HttpClientsLock); + boost::mutex::scoped_lock lock(m_HttpClientsLock); m_HttpClients.insert(aclient); } void ApiListener::RemoveHttpClient(const HttpServerConnection::Ptr& aclient) { - boost::mutex::scoped_lock(m_HttpClientsLock); + boost::mutex::scoped_lock lock(m_HttpClientsLock); m_HttpClients.erase(aclient); } std::set ApiListener::GetHttpClients(void) const { - boost::mutex::scoped_lock(m_HttpClientsLock); + boost::mutex::scoped_lock lock(m_HttpClientsLock); return m_HttpClients; } From de7a90dba770a2802fcdaa182ddfa1e704ba7328 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 21 Nov 2017 09:38:56 +0100 Subject: [PATCH 02/35] ITL: Drop ssl_sni default setting That's not supported by all plugin suites out there, and as such should not be the default value. The user must set it explicitly. fixes #5775 refs #5785 --- doc/10-icinga-template-library.md | 2 +- itl/command-plugins.conf | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 17bb38b71..60a6a3960 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -1307,7 +1307,7 @@ ssl_port | **Optional.** The port that should be checked. D ssl_timeout | **Optional.** Timeout in seconds for the connect and handshake. The plugin default is 10 seconds. ssl_cert_valid_days_warn | **Optional.** Warning threshold for days before the certificate will expire. When used, the default for ssl_cert_valid_days_critical is 0. ssl_cert_valid_days_critical | **Optional.** Critical threshold for days before the certificate will expire. When used, ssl_cert_valid_days_warn must also be set. -ssl_sni | **Optional.** The `server_name` that is send to select the SSL certificate to check. Important if SNI is used. Defaults to "$ssl_address$". +ssl_sni | **Optional.** The `server_name` that is send to select the SSL certificate to check. Important if SNI is used. ### ssmtp diff --git a/itl/command-plugins.conf b/itl/command-plugins.conf index f4c01134c..017667893 100644 --- a/itl/command-plugins.conf +++ b/itl/command-plugins.conf @@ -350,7 +350,6 @@ object CheckCommand "ssl" { vars.ssl_port = 443 vars.ssl_cert_valid_days_warn = false vars.ssl_cert_valid_days_critical = false - vars.ssl_sni = "$ssl_address$" } From 5b231147fe11f1cb702386b61a84c094ab416239 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 27 Nov 2017 12:09:42 +0100 Subject: [PATCH 03/35] Silence UpdateRepository message errors Mixed setups with 2.8 and 2.7 will log this error every time a new message is received. While this is truly an error, it just fills the log to infinity. Should be used for debug only. fixes #5776 refs #5804 --- lib/remote/jsonrpcconnection.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/remote/jsonrpcconnection.cpp b/lib/remote/jsonrpcconnection.cpp index 3d9cacaf0..e6cd9a3c8 100644 --- a/lib/remote/jsonrpcconnection.cpp +++ b/lib/remote/jsonrpcconnection.cpp @@ -208,11 +208,12 @@ void JsonRpcConnection::MessageHandler(const String& jsonString) try { ApiFunction::Ptr afunc = ApiFunction::GetByName(method); - if (!afunc) - Log(LogWarning, "JsonRpcConnection") + if (!afunc) { + Log(LogNotice, "JsonRpcConnection") << "Call to non-existent function '" << method << "' from endpoint '" << m_Identity << "'."; - else + } else { resultMessage->Set("result", afunc->Invoke(origin, message->Get("params"))); + } } catch (const std::exception& ex) { /* TODO: Add a user readable error message for the remote caller */ String diagInfo = DiagnosticInformation(ex); From f4524c2abaaa4157bfa47deea4b00ff72e1148ce Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 20 Nov 2017 18:48:26 +0100 Subject: [PATCH 04/35] Fix formatting in value types docs refs #5783 --- doc/03-monitoring-basics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/03-monitoring-basics.md b/doc/03-monitoring-basics.md index e66ab7dde..fc768cd88 100644 --- a/doc/03-monitoring-basics.md +++ b/doc/03-monitoring-basics.md @@ -16,8 +16,8 @@ The Icinga 2 configuration uses different value types for attributes. [Duration](17-language-reference.md#duration-literals) | `1m` [String](17-language-reference.md#string-literals) | `"These are notes"` [Boolean](17-language-reference.md#boolean-literals) | `true` - [Array](17-language-reference.md#array) | `[ "value1", "value2" ]`) - [Dictionary](17-language-reference.md#dictionary) | `{ "key1" = "value1", "key2" = false }` ) + [Array](17-language-reference.md#array) | `[ "value1", "value2" ]` + [Dictionary](17-language-reference.md#dictionary) | `{ "key1" = "value1", "key2" = false }` It is important to use the correct value type for object attributes as otherwise the [configuration validation](11-cli-commands.md#config-validation) will fail. From 83d6cdb27b6e7f8b195edeb07f83f2e2a9a6b211 Mon Sep 17 00:00:00 2001 From: Strix <660956+MrStrix@users.noreply.github.com> Date: Tue, 5 Dec 2017 11:31:34 +0100 Subject: [PATCH 05/35] fix formatting error --- doc/02-getting-started.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/02-getting-started.md b/doc/02-getting-started.md index 9c67e2487..f8664cbc5 100644 --- a/doc/02-getting-started.md +++ b/doc/02-getting-started.md @@ -644,10 +644,10 @@ FreeBSD: Alpine Linux: - # apk add postgresql - # rc-update add postgresql default - # rc-service postgresql setup - # rc-service postgresql start + # apk add postgresql + # rc-update add postgresql default + # rc-service postgresql setup + # rc-service postgresql start #### Installing the IDO modules for PostgreSQL From 89f905acef49724980da22fbfc2a2faa3fe6ab2a Mon Sep 17 00:00:00 2001 From: Dirk Goetz Date: Wed, 6 Dec 2017 09:00:07 +0100 Subject: [PATCH 06/35] fixes postgres schema upgrade path refs #5835 --- doc/16-upgrading-icinga-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/16-upgrading-icinga-2.md b/doc/16-upgrading-icinga-2.md index 5bff0eff2..56dce46e4 100644 --- a/doc/16-upgrading-icinga-2.md +++ b/doc/16-upgrading-icinga-2.md @@ -232,7 +232,7 @@ which must be applied incrementally to your IDO database. ## Upgrading the PostgreSQL database If you want to upgrade an existing Icinga 2 instance, check the -`/usr/share/icinga2-ido-mysql/schema/upgrade` directory for incremental schema upgrade file(s). +`/usr/share/icinga2-ido-pgsql/schema/upgrade` directory for incremental schema upgrade file(s). > **Note** > From 3a7c54c3d1e5be424fdccef6383f53fde40e5005 Mon Sep 17 00:00:00 2001 From: Paolo Schiro Date: Fri, 3 Nov 2017 01:14:05 +0100 Subject: [PATCH 07/35] add a bogus zero reply in livestatus if aggregate filter does not match, fix I#5626 refs #5716 --- lib/livestatus/livestatusquery.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/livestatus/livestatusquery.cpp b/lib/livestatus/livestatusquery.cpp index 2427d40e7..3132fe532 100644 --- a/lib/livestatus/livestatusquery.cpp +++ b/lib/livestatus/livestatusquery.cpp @@ -571,6 +571,17 @@ void LivestatusQuery::ExecuteGetHelper(const Stream::Ptr& stream) AppendResultRow(result, row, first_row); } + + /* add a bogus zero value if aggregated is empty*/ + if (allStats.empty()) { + Array::Ptr row = new Array(); + + for (size_t i = 1; i <= m_Aggregators.size(); i++) { + row->Add(0); + } + + AppendResultRow(result, row, first_row); + } } EndResultSet(result); From 9ba5f30afa88cbf5273073e8563dd5873addc73a Mon Sep 17 00:00:00 2001 From: Sven Nierlein Date: Mon, 30 Oct 2017 14:49:22 +0100 Subject: [PATCH 08/35] livestatus: custom variables return empty arrays instead of strings livestatus queries for custom variables should return an empty list, ex: [] instead of an empty string if there are no variables. Signed-off-by: Sven Nierlein refs #5746 --- lib/livestatus/commandstable.cpp | 18 +++++++++--------- lib/livestatus/contactstable.cpp | 18 +++++++++--------- lib/livestatus/hoststable.cpp | 18 +++++++++--------- lib/livestatus/servicestable.cpp | 18 +++++++++--------- lib/livestatus/statustable.cpp | 18 +++++++++--------- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/lib/livestatus/commandstable.cpp b/lib/livestatus/commandstable.cpp index d41905ede..f3891086f 100644 --- a/lib/livestatus/commandstable.cpp +++ b/lib/livestatus/commandstable.cpp @@ -106,11 +106,11 @@ Value CommandsTable::CustomVariableNamesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(command); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + { ObjectLock xlock(vars); for (const auto& kv : vars) { @@ -135,11 +135,11 @@ Value CommandsTable::CustomVariableValuesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(command); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + { ObjectLock xlock(vars); for (const auto& kv : vars) { @@ -164,11 +164,11 @@ Value CommandsTable::CustomVariablesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(command); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + { ObjectLock xlock(vars); for (const auto& kv : vars) { diff --git a/lib/livestatus/contactstable.cpp b/lib/livestatus/contactstable.cpp index e83127d75..c83865a93 100644 --- a/lib/livestatus/contactstable.cpp +++ b/lib/livestatus/contactstable.cpp @@ -210,11 +210,11 @@ Value ContactsTable::CustomVariableNamesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(user); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + ObjectLock olock(vars); for (const Dictionary::Pair& kv : vars) { cv->Add(kv.first); @@ -237,11 +237,11 @@ Value ContactsTable::CustomVariableValuesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(user); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + ObjectLock olock(vars); for (const Dictionary::Pair& kv : vars) { if (kv.second.IsObjectType() || kv.second.IsObjectType()) @@ -267,11 +267,11 @@ Value ContactsTable::CustomVariablesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(user); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + ObjectLock olock(vars); for (const Dictionary::Pair& kv : vars) { Array::Ptr key_val = new Array(); diff --git a/lib/livestatus/hoststable.cpp b/lib/livestatus/hoststable.cpp index b7199b4b1..7e4a5bb30 100644 --- a/lib/livestatus/hoststable.cpp +++ b/lib/livestatus/hoststable.cpp @@ -1016,11 +1016,11 @@ Value HostsTable::CustomVariableNamesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(host); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + ObjectLock olock(vars); for (const Dictionary::Pair& kv : vars) { cv->Add(kv.first); @@ -1043,11 +1043,11 @@ Value HostsTable::CustomVariableValuesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(host); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + ObjectLock olock(vars); for (const Dictionary::Pair& kv : vars) { if (kv.second.IsObjectType() || kv.second.IsObjectType()) @@ -1073,11 +1073,11 @@ Value HostsTable::CustomVariablesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(host); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + ObjectLock olock(vars); for (const Dictionary::Pair& kv : vars) { Array::Ptr key_val = new Array(); diff --git a/lib/livestatus/servicestable.cpp b/lib/livestatus/servicestable.cpp index 74acd8cf8..e2edf8481 100644 --- a/lib/livestatus/servicestable.cpp +++ b/lib/livestatus/servicestable.cpp @@ -1055,11 +1055,11 @@ Value ServicesTable::CustomVariableNamesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(service); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + ObjectLock olock(vars); for (const Dictionary::Pair& kv : vars) { cv->Add(kv.first); @@ -1082,11 +1082,11 @@ Value ServicesTable::CustomVariableValuesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(service); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + ObjectLock olock(vars); for (const Dictionary::Pair& kv : vars) { if (kv.second.IsObjectType() || kv.second.IsObjectType()) @@ -1112,11 +1112,11 @@ Value ServicesTable::CustomVariablesAccessor(const Value& row) vars = CompatUtility::GetCustomAttributeConfig(service); } - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + ObjectLock olock(vars); for (const Dictionary::Pair& kv : vars) { Array::Ptr key_val = new Array(); diff --git a/lib/livestatus/statustable.cpp b/lib/livestatus/statustable.cpp index f9f7b57fa..90e311bf5 100644 --- a/lib/livestatus/statustable.cpp +++ b/lib/livestatus/statustable.cpp @@ -233,11 +233,11 @@ Value StatusTable::CustomVariableNamesAccessor(const Value&) { Dictionary::Ptr vars = IcingaApplication::GetInstance()->GetVars(); - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + { ObjectLock olock(vars); for (const auto& kv : vars) { @@ -252,11 +252,11 @@ Value StatusTable::CustomVariableValuesAccessor(const Value&) { Dictionary::Ptr vars = IcingaApplication::GetInstance()->GetVars(); - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + { ObjectLock olock(vars); for (const auto& kv : vars) { @@ -271,11 +271,11 @@ Value StatusTable::CustomVariablesAccessor(const Value&) { Dictionary::Ptr vars = IcingaApplication::GetInstance()->GetVars(); - if (!vars) - return Empty; - Array::Ptr cv = new Array(); + if (!vars) + return cv; + { ObjectLock olock(vars); for (const auto& kv : vars) { From 806eb0d2d2b077a9a4bac073bfbb27c2af369008 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Thu, 7 Dec 2017 11:11:03 +0100 Subject: [PATCH 09/35] Fix newline terminator for bulk requests in ElasticsearchWriter This enables compatibility with 6.x. This commit also fixes an incorrect HTTP response parsing which could lead into false positives. Analysis and fix in https://github.com/Icinga/icinga2/issues/5795#issuecomment-349920587 fixes #5795 refs #5840 --- lib/perfdata/elasticsearchwriter.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/perfdata/elasticsearchwriter.cpp b/lib/perfdata/elasticsearchwriter.cpp index 69221fd88..2171752ca 100644 --- a/lib/perfdata/elasticsearchwriter.cpp +++ b/lib/perfdata/elasticsearchwriter.cpp @@ -395,6 +395,11 @@ void ElasticsearchWriter::Flush(void) String body = boost::algorithm::join(m_DataBuffer, "\n"); m_DataBuffer.clear(); + /* Elasticsearch 6.x requires a new line. This is compatible to 5.x. + * Tested with 6.0.0 and 5.6.4. + */ + body += "\n"; + SendRequest(body); } @@ -454,12 +459,20 @@ void ElasticsearchWriter::SendRequest(const String& body) try { resp.Parse(context, true); + while (resp.Parse(context, true) && !resp.Complete) + ; /* Do nothing */ } catch (const std::exception& ex) { Log(LogWarning, "ElasticsearchWriter") - << "Cannot read from HTTP API on host '" << GetHost() << "' port '" << GetPort() << "'."; + << "Failed to parse HTTP response from host '" << GetHost() << "' port '" << GetPort() << "': " << DiagnosticInformation(ex, false); throw ex; } + if (!resp.Complete) { + Log(LogWarning, "ElasticsearchWriter") + << "Failed to read a complete HTTP response from the Elasticsearch server."; + return; + } + if (resp.StatusCode > 299) { if (resp.StatusCode == 401) { /* More verbose error logging with Elasticsearch is hidden behind a proxy. */ @@ -478,10 +491,6 @@ void ElasticsearchWriter::SendRequest(const String& body) Log(LogWarning, "ElasticsearchWriter") << "Unexpected response code " << resp.StatusCode; - /* Finish parsing the headers and body. */ - while (!resp.Complete) - resp.Parse(context, true); - String contentType = resp.Headers->Get("content-type"); if (contentType != "application/json") { @@ -508,6 +517,8 @@ void ElasticsearchWriter::SendRequest(const String& body) Log(LogCritical, "ElasticsearchWriter") << "Elasticsearch error message:\n" << error; + + return; } } From 9b0cc125dd9d893a7e45547a252aacd03cea845c Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Thu, 7 Dec 2017 13:31:07 +0100 Subject: [PATCH 10/35] Update docs for Elasticsearch 5.x and 6.x support refs #5840 --- doc/14-features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/14-features.md b/doc/14-features.md index e2fdeb591..316707a3e 100644 --- a/doc/14-features.md +++ b/doc/14-features.md @@ -347,7 +347,7 @@ The check results include parsed performance data metrics if enabled. > **Note** > -> Elasticsearch 5.x+ is required. +> Elasticsearch 5.x+ is required. This feature has been tested with Elasticsearch 5.6.4 and 6.0.0. Enable the feature and restart Icinga 2. From eea451ee94be7310e63ab6b7e5e94924b295e974 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 24 Oct 2017 15:23:58 +0200 Subject: [PATCH 11/35] Fix performance issues for InfluxdbWriter --- lib/perfdata/influxdbwriter.cpp | 182 +++++++++++++++----------------- lib/perfdata/influxdbwriter.hpp | 12 +-- 2 files changed, 90 insertions(+), 104 deletions(-) diff --git a/lib/perfdata/influxdbwriter.cpp b/lib/perfdata/influxdbwriter.cpp index aa35fec0e..499e77a89 100644 --- a/lib/perfdata/influxdbwriter.cpp +++ b/lib/perfdata/influxdbwriter.cpp @@ -49,6 +49,24 @@ using namespace icinga; +class InfluxdbInteger : public Object +{ +public: + DECLARE_PTR_TYPEDEFS(InfluxdbInteger); + + InfluxdbInteger(int value) + : m_Value(value) + { } + + int GetValue(void) const + { + return m_Value; + } + +private: + int m_Value; +}; + REGISTER_TYPE(InfluxdbWriter); REGISTER_STATSFUNCTION(InfluxdbWriter, &InfluxdbWriter::StatsFunc); @@ -106,7 +124,7 @@ void InfluxdbWriter::Start(bool runtimeCreated) m_FlushTimer->Reschedule(0); /* Register for new metrics. */ - Service::OnNewCheckResult.connect(boost::bind(&InfluxdbWriter::CheckResultHandler, this, _1, _2)); + Checkable::OnNewCheckResult.connect(boost::bind(&InfluxdbWriter::CheckResultHandler, this, _1, _2)); } void InfluxdbWriter::Stop(bool runtimeRemoved) @@ -134,7 +152,7 @@ void InfluxdbWriter::ExceptionHandler(boost::exception_ptr exp) //TODO: Close the connection, if we keep it open. } -Stream::Ptr InfluxdbWriter::Connect() +Stream::Ptr InfluxdbWriter::Connect(void) { TcpSocket::Ptr socket = new TcpSocket(); @@ -176,10 +194,10 @@ Stream::Ptr InfluxdbWriter::Connect() void InfluxdbWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { - m_WorkQueue.Enqueue(boost::bind(&InfluxdbWriter::InternalCheckResultHandler, this, checkable, cr)); + m_WorkQueue.Enqueue(boost::bind(&InfluxdbWriter::CheckResultHandlerWQ, this, checkable, cr), PriorityLow); } -void InfluxdbWriter::InternalCheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) +void InfluxdbWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { AssertOnWorkQueue(); @@ -211,28 +229,16 @@ void InfluxdbWriter::InternalCheckResultHandler(const Checkable::Ptr& checkable, if (tags) { ObjectLock olock(tags); for (const Dictionary::Pair& pair : tags) { - // Prevent missing macros from warning; will return an empty value - // which will be filtered out in SendMetric() String missing_macro; - tags->Set(pair.first, MacroProcessor::ResolveMacros(pair.second, resolvers, cr, &missing_macro)); + Value value = MacroProcessor::ResolveMacros(pair.second, resolvers, cr, &missing_macro); + + if (!missing_macro.IsEmpty()) + continue; + + tags->Set(pair.first, value); } } - SendPerfdata(tmpl, checkable, cr, ts); -} - -String InfluxdbWriter::FormatInteger(int val) -{ - return Convert::ToString(val) + "i"; -} - -String InfluxdbWriter::FormatBoolean(bool val) -{ - return val ? "true" : "false"; -} - -void InfluxdbWriter::SendPerfdata(const Dictionary::Ptr& tmpl, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, double ts) -{ Array::Ptr perfdata = cr->GetPerformanceData(); if (perfdata) { ObjectLock olock(perfdata); @@ -277,16 +283,16 @@ void InfluxdbWriter::SendPerfdata(const Dictionary::Ptr& tmpl, const Checkable:: Dictionary::Ptr fields = new Dictionary(); if (service) - fields->Set("state", FormatInteger(service->GetState())); + fields->Set("state", new InfluxdbInteger(service->GetState())); else - fields->Set("state", FormatInteger(host->GetState())); + fields->Set("state", new InfluxdbInteger(host->GetState())); - fields->Set("current_attempt", FormatInteger(checkable->GetCheckAttempt())); - fields->Set("max_check_attempts", FormatInteger(checkable->GetMaxCheckAttempts())); - fields->Set("state_type", FormatInteger(checkable->GetStateType())); - fields->Set("reachable", FormatBoolean(checkable->IsReachable())); - fields->Set("downtime_depth", FormatInteger(checkable->GetDowntimeDepth())); - fields->Set("acknowledgement", FormatInteger(checkable->GetAcknowledgement())); + fields->Set("current_attempt", new InfluxdbInteger(checkable->GetCheckAttempt())); + fields->Set("max_check_attempts", new InfluxdbInteger(checkable->GetMaxCheckAttempts())); + fields->Set("state_type", new InfluxdbInteger(checkable->GetStateType())); + fields->Set("reachable", checkable->IsReachable()); + fields->Set("downtime_depth", new InfluxdbInteger(checkable->GetDowntimeDepth())); + fields->Set("acknowledgement", new InfluxdbInteger(checkable->GetAcknowledgement())); fields->Set("latency", cr->CalculateLatency()); fields->Set("execution_time", cr->CalculateExecutionTime()); @@ -294,7 +300,7 @@ void InfluxdbWriter::SendPerfdata(const Dictionary::Ptr& tmpl, const Checkable:: } } -String InfluxdbWriter::EscapeKey(const String& str) +String InfluxdbWriter::EscapeKeyOrTagValue(const String& str) { // Iterate over the key name and escape commas and spaces with a backslash String result = str; @@ -302,57 +308,28 @@ String InfluxdbWriter::EscapeKey(const String& str) boost::algorithm::replace_all(result, "=", "\\="); boost::algorithm::replace_all(result, ",", "\\,"); boost::algorithm::replace_all(result, " ", "\\ "); - - // InfluxDB 'feature': although backslashes are allowed in keys they also act - // as escape sequences when followed by ',' or ' '. When your tag is like - // 'metric=C:\' bad things happen. Backslashes themselves cannot be escaped - // and through experimentation they also escape '='. To be safe we replace - // trailing backslashes with and underscore. - size_t length = result.GetLength(); - if (result[length - 1] == '\\') - result[length - 1] = '_'; - return result; } -String InfluxdbWriter::EscapeField(const String& str) +String InfluxdbWriter::EscapeValue(const Value& value) { - //TODO: Evaluate whether boost::regex is really needed here. - - // Handle integers - boost::regex integer("-?\\d+i"); - if (boost::regex_match(str.GetData(), integer)) { - return str; + if (value.IsObjectType()) { + std::ostringstream os; + os << static_cast(value)->GetValue() + << "i"; + return os.str(); } - // Handle numerics - boost::regex numeric("-?\\d+(\\.\\d+)?((e|E)[+-]?\\d+)?"); - if (boost::regex_match(str.GetData(), numeric)) { - return str; - } + if (value.IsBoolean()) + return value ? "true" : "false"; - // Handle booleans - boost::regex boolean_true("t|true", boost::regex::icase); - if (boost::regex_match(str.GetData(), boolean_true)) - return "true"; - boost::regex boolean_false("f|false", boost::regex::icase); - if (boost::regex_match(str.GetData(), boolean_false)) - return "false"; - - // Handle NaNs - if (boost::math::isnan(str)) - return 0; - - // Otherwise it's a string and needs escaping and quoting - String result = str; - boost::algorithm::replace_all(result, "\"", "\\\""); - return "\"" + result + "\""; + return value; } void InfluxdbWriter::SendMetric(const Dictionary::Ptr& tmpl, const String& label, const Dictionary::Ptr& fields, double ts) { std::ostringstream msgbuf; - msgbuf << EscapeKey(tmpl->Get("measurement")); + msgbuf << EscapeKeyOrTagValue(tmpl->Get("measurement")); Dictionary::Ptr tags = tmpl->Get("tags"); if (tags) { @@ -360,14 +337,14 @@ void InfluxdbWriter::SendMetric(const Dictionary::Ptr& tmpl, const String& label for (const Dictionary::Pair& pair : tags) { // Empty macro expansion, no tag if (!pair.second.IsEmpty()) { - msgbuf << "," << EscapeKey(pair.first) << "=" << EscapeKey(pair.second); + msgbuf << "," << EscapeKeyOrTagValue(pair.first) << "=" << EscapeKeyOrTagValue(pair.second); } } } - // Label is may be empty in the case of metadata + // Label may be empty in the case of metadata if (!label.IsEmpty()) - msgbuf << ",metric=" << EscapeKey(label); + msgbuf << ",metric=" << EscapeKeyOrTagValue(label); msgbuf << " "; @@ -381,45 +358,54 @@ void InfluxdbWriter::SendMetric(const Dictionary::Ptr& tmpl, const String& label else msgbuf << ","; - msgbuf << EscapeKey(pair.first) << "=" << EscapeField(pair.second); + msgbuf << EscapeKeyOrTagValue(pair.first) << "=" << EscapeValue(pair.second); } } msgbuf << " " << static_cast(ts); +#ifdef I2_DEBUG Log(LogDebug, "InfluxdbWriter") << "Add to metric list: '" << msgbuf.str() << "'."; +#endif /* I2_DEBUG */ - // Atomically buffer the data point - boost::mutex::scoped_lock lock(m_DataBufferMutex); - m_DataBuffer.push_back(String(msgbuf.str())); + // Buffer the data point + m_DataBuffer.push_back(msgbuf.str()); // Flush if we've buffered too much to prevent excessive memory use if (static_cast(m_DataBuffer.size()) >= GetFlushThreshold()) { Log(LogDebug, "InfluxdbWriter") << "Data buffer overflow writing " << m_DataBuffer.size() << " data points"; - Flush(); + + try { + Flush(); + } catch (...) { + /* Do nothing. */ + } } } void InfluxdbWriter::FlushTimeout(void) { - // Prevent new data points from being added to the array, there is a - // race condition where they could disappear - boost::mutex::scoped_lock lock(m_DataBufferMutex); + m_WorkQueue.Enqueue(boost::bind(&InfluxdbWriter::FlushTimeoutWQ, this), PriorityHigh); +} + +void InfluxdbWriter::FlushTimeoutWQ(void) +{ + AssertOnWorkQueue(); // Flush if there are any data available - if (m_DataBuffer.size() > 0) { - Log(LogDebug, "InfluxdbWriter") - << "Timer expired writing " << m_DataBuffer.size() << " data points"; - Flush(); - } + if (m_DataBuffer.empty()) + return; + + Log(LogDebug, "InfluxdbWriter") + << "Timer expired writing " << m_DataBuffer.size() << " data points"; + + Flush(); } void InfluxdbWriter::Flush(void) { - // Ensure you hold a lock against m_DataBuffer so that things - // don't go missing after creating the body and clearing the buffer String body = boost::algorithm::join(m_DataBuffer, "\n"); m_DataBuffer.clear(); @@ -457,25 +443,27 @@ void InfluxdbWriter::Flush(void) throw ex; } - //TODO: Evaluate whether waiting for the result makes sense here. KeepAlive and close are options. HttpResponse resp(stream, req); StreamReadContext context; try { - resp.Parse(context, true); + while (resp.Parse(context, true) && !resp.Complete) + ; /* Do nothing */ } catch (const std::exception& ex) { Log(LogWarning, "InfluxdbWriter") - << "Cannot read from TCP socket from host '" << GetHost() << "' port '" << GetPort() << "'."; + << "Failed to parse HTTP response from host '" << GetHost() << "' port '" << GetPort() << "': " << DiagnosticInformation(ex); throw ex; } + if (!resp.Complete) { + Log(LogWarning, "InfluxdbWriter") + << "Failed to read a complete HTTP response from the InfluxDB server."; + return; + } + if (resp.StatusCode != 204) { Log(LogWarning, "InfluxdbWriter") - << "Unexpected response code " << resp.StatusCode; - - // Finish parsing the headers and body - while (!resp.Complete) - resp.Parse(context, true); + << "Unexpected response code: " << resp.StatusCode; String contentType = resp.Headers->Get("content-type"); if (contentType != "application/json") { @@ -502,6 +490,8 @@ void InfluxdbWriter::Flush(void) Log(LogCritical, "InfluxdbWriter") << "InfluxDB error message:\n" << error; + + return; } } diff --git a/lib/perfdata/influxdbwriter.hpp b/lib/perfdata/influxdbwriter.hpp index e0a149c3a..97546bc84 100644 --- a/lib/perfdata/influxdbwriter.hpp +++ b/lib/perfdata/influxdbwriter.hpp @@ -59,20 +59,16 @@ private: WorkQueue m_WorkQueue; Timer::Ptr m_FlushTimer; std::vector m_DataBuffer; - boost::mutex m_DataBufferMutex; void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); - void InternalCheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); - void SendPerfdata(const Dictionary::Ptr& tmpl, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, double ts); + void CheckResultHandlerWQ(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); void SendMetric(const Dictionary::Ptr& tmpl, const String& label, const Dictionary::Ptr& fields, double ts); void FlushTimeout(void); + void FlushTimeoutWQ(void); void Flush(void); - static String FormatInteger(int val); - static String FormatBoolean(bool val); - - static String EscapeKey(const String& str); - static String EscapeField(const String& str); + static String EscapeKeyOrTagValue(const String& str); + static String EscapeValue(const Value& value); Stream::Ptr Connect(); From 59da943548c7c1be5f4b27dce73a0ad27cf0de5d Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 14 Nov 2017 14:13:24 +0100 Subject: [PATCH 12/35] Fix incorrect socket handling for the HTTP client --- lib/base/stream.cpp | 3 +++ lib/remote/httpresponse.cpp | 39 +++++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/lib/base/stream.cpp b/lib/base/stream.cpp index 9390d97b9..57791d305 100644 --- a/lib/base/stream.cpp +++ b/lib/base/stream.cpp @@ -145,6 +145,9 @@ bool StreamReadContext::FillFromStream(const Stream::Ptr& stream, bool may_wait) if (!Buffer) throw std::bad_alloc(); + if (stream->IsEof()) + break; + size_t rc = stream->Read(Buffer + Size, 4096, true); Size += rc; diff --git a/lib/remote/httpresponse.cpp b/lib/remote/httpresponse.cpp index 7f77ed386..ab7458655 100644 --- a/lib/remote/httpresponse.cpp +++ b/lib/remote/httpresponse.cpp @@ -159,12 +159,7 @@ bool HttpResponse::Parse(StreamReadContext& src, bool may_wait) if (line == "") { m_State = HttpResponseBody; - - /* we're done if the request doesn't contain a message body */ - if (!Headers->Contains("content-length") && !Headers->Contains("transfer-encoding")) - Complete = true; - else - m_Body = new FIFO(); + m_Body = new FIFO(); return true; @@ -204,27 +199,41 @@ bool HttpResponse::Parse(StreamReadContext& src, bool may_wait) return true; } } else { - if (src.Eof) + bool hasLengthIndicator = false; + size_t lengthIndicator = 0; + Value contentLengthHeader; + + if (Headers->Get("content-length", &contentLengthHeader)) { + hasLengthIndicator = true; + lengthIndicator = Convert::ToLong(contentLengthHeader); + } + + if (hasLengthIndicator && src.Eof) BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); if (src.MustRead) { - if (!src.FillFromStream(m_Stream, false)) { + if (!src.FillFromStream(m_Stream, may_wait)) src.Eof = true; - BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); - } src.MustRead = false; } - size_t length_indicator = Convert::ToLong(Headers->Get("content-length")); + if (!hasLengthIndicator) + lengthIndicator = src.Size; - if (src.Size < length_indicator) { + if (src.Size < lengthIndicator) { src.MustRead = true; - return false; + return may_wait; + } + + m_Body->Write(src.Buffer, lengthIndicator); + src.DropData(lengthIndicator); + + if (!hasLengthIndicator && !src.Eof) { + src.MustRead = true; + return may_wait; } - m_Body->Write(src.Buffer, length_indicator); - src.DropData(length_indicator); Complete = true; return true; } From 50eb774a0f57c8a09c0583e2be204bcc5bfd58cf Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 15 Nov 2017 09:47:23 +0100 Subject: [PATCH 13/35] Make default getters and setters non-virtual --- tools/mkclass/class_lexer.ll | 3 +++ tools/mkclass/classcompiler.cpp | 25 ++++++++++++++++++------- tools/mkclass/classcompiler.hpp | 4 +++- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/mkclass/class_lexer.ll b/tools/mkclass/class_lexer.ll index 0b9195464..625a99f22 100644 --- a/tools/mkclass/class_lexer.ll +++ b/tools/mkclass/class_lexer.ll @@ -148,6 +148,9 @@ no_storage { yylval->num = FANoStorage; return T_FIELD_ATTRIBUTE; } no_user_modify { yylval->num = FANoUserModify; return T_FIELD_ATTRIBUTE; } no_user_view { yylval->num = FANoUserView; return T_FIELD_ATTRIBUTE; } deprecated { yylval->num = FADeprecated; return T_FIELD_ATTRIBUTE; } +get_virtual { yylval->num = FAGetVirtual; return T_FIELD_ATTRIBUTE; } +set_virtual { yylval->num = FASetVirtual; return T_FIELD_ATTRIBUTE; } +virtual { yylval->num = FAGetVirtual | FASetVirtual; return T_FIELD_ATTRIBUTE; } navigation { return T_NAVIGATION; } validator { return T_VALIDATOR; } required { return T_REQUIRED; } diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index 04e9abdeb..7d068bcd5 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -706,7 +706,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) /* NotifyField */ m_Header << "public:" << std::endl - << "\t" << "virtual void NotifyField(int id, const Value& cookie = Empty) override;" << std::endl; + << "\t" << "void NotifyField(int id, const Value& cookie = Empty) override;" << std::endl; m_Impl << "void ObjectImpl<" << klass.Name << ">::NotifyField(int id, const Value& cookie)" << std::endl << "{" << std::endl; @@ -784,7 +784,12 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) prot = "public"; m_Header << prot << ":" << std::endl - << "\t" << "virtual " << field.Type.GetRealType() << " Get" << field.GetFriendlyName() << "(void) const"; + << "\t"; + + if (field.Attributes & FAGetVirtual || field.PureGetAccessor) + m_Header << "virtual "; + + m_Header << field.Type.GetRealType() << " Get" << field.GetFriendlyName() << "(void) const"; if (field.PureGetAccessor) { m_Header << " = 0;" << std::endl; @@ -813,7 +818,12 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) prot = "public"; m_Header << prot << ":" << std::endl - << "\t" << "virtual void Set" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " value, bool suppress_events = false, const Value& cookie = Empty)"; + << "\t"; + + if (field.Attributes & FASetVirtual || field.PureSetAccessor) + m_Header << "virtual "; + + m_Header << "void Set" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " value, bool suppress_events = false, const Value& cookie = Empty)"; if (field.PureSetAccessor) { m_Header << " = 0;" << std::endl; @@ -859,7 +869,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) needs_tracking = true; - m_Header << "\t" << "virtual void Track" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " oldValue, " << field.Type.GetArgumentType() << " newValue);"; + m_Header << "\t" << "void Track" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " oldValue, " << field.Type.GetArgumentType() << " newValue);" << std::endl; m_Impl << "void ObjectImpl<" << klass.Name << ">::Track" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " oldValue, " << field.Type.GetArgumentType() << " newValue)" << std::endl << "{" << std::endl; @@ -930,7 +940,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) continue; m_Header << "public:" << std::endl - << "\t" << "virtual Object::Ptr Navigate" << field.GetFriendlyName() << "(void) const"; + << "\t" << "Object::Ptr Navigate" << field.GetFriendlyName() << "(void) const"; if (field.PureNavigateAccessor) { m_Header << " = 0;" << std::endl; @@ -951,8 +961,9 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) /* start/stop */ if (needs_tracking) { - m_Header << "virtual void Start(bool runtimeCreated = false) override;" << std::endl - << "virtual void Stop(bool runtimeRemoved = false) override;" << std::endl; + m_Header << "protected:" << std::endl + << "\tvirtual void Start(bool runtimeCreated = false) override;" << std::endl + << "\tvirtual void Stop(bool runtimeRemoved = false) override;" << std::endl; m_Impl << "void ObjectImpl<" << klass.Name << ">::Start(bool runtimeCreated)" << std::endl << "{" << std::endl diff --git a/tools/mkclass/classcompiler.hpp b/tools/mkclass/classcompiler.hpp index 06bf100d3..ab9422220 100644 --- a/tools/mkclass/classcompiler.hpp +++ b/tools/mkclass/classcompiler.hpp @@ -73,7 +73,9 @@ enum FieldAttribute FANavigation = 512, FANoUserModify = 1024, FANoUserView = 2048, - FADeprecated = 4096 + FADeprecated = 4096, + FAGetVirtual = 8192, + FASetVirtual = 16384 }; struct FieldType From 64355744131986420e9492bd84e7f04e46e09765 Mon Sep 17 00:00:00 2001 From: Max Rosin Date: Wed, 15 Nov 2017 01:03:15 +0100 Subject: [PATCH 14/35] Add documentation about automatic service restarts with systemd refs #5721 refs #5757 --- doc/02-getting-started.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/02-getting-started.md b/doc/02-getting-started.md index f8664cbc5..f39dac122 100644 --- a/doc/02-getting-started.md +++ b/doc/02-getting-started.md @@ -347,6 +347,24 @@ Examples: If you're stuck with configuration errors, you can manually invoke the [configuration validation](11-cli-commands.md#config-validation). +Usually Icinga 2 is a mission critical part of infrastructure and should be +online at all times. In case of a recoverable crash (e.g. OOM) you may want to +restart Icinga 2 automatically. With Systemd it is as easy as overriding some +settings of the Icinga 2 Systemd service by creating +`/etc/systemd/system/icinga2.service.d/override.conf` with the following +content: + + [Service] + Restart=always + RestartSec=1 + StartLimitInterval=10 + StartLimitBurst=3 + +Run `systemctl daemon-reload && systemctl restart icinga2` to apply the changes. +Now Systemd will always try to restart Icinga 2 (except if you run +`systemctl stop icinga2`). After three failures in ten seconds it will stop +trying because you probably have a problem that requires manual intervention. + > **Tip** > > If you are running into fork errors with Systemd enabled distributions, From 3640d7545cfa0e7bd18320af263019f79f6d17da Mon Sep 17 00:00:00 2001 From: noobahoi <20069422+noobahoi@users.noreply.github.com> Date: Tue, 21 Nov 2017 16:17:58 +0100 Subject: [PATCH 15/35] Update 09-object-types.md 'TicketSalt' instead of 'NodeName' refs #5790 --- doc/09-object-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/09-object-types.md b/doc/09-object-types.md index f442c376e..3922f92b8 100644 --- a/doc/09-object-types.md +++ b/doc/09-object-types.md @@ -35,7 +35,7 @@ and API usage specifying the certificate files used for ssl authorization and additional restrictions. This configuration object is available as [api feature](11-cli-commands.md#cli-command-feature). -The `NodeName` constant must be defined in [constants.conf](04-configuring-icinga-2.md#constants-conf). +The `TicketSalt` constant must be defined in [constants.conf](04-configuring-icinga-2.md#constants-conf). Example: From 02ecf4d7288252c18ff85e08e9f77653421e11cf Mon Sep 17 00:00:00 2001 From: noobahoi <20069422+noobahoi@users.noreply.github.com> Date: Tue, 21 Nov 2017 16:22:58 +0100 Subject: [PATCH 16/35] Update 09-object-types.md object Downtime "localhost!my-downtime" should be object Downtime "my-downtime" The host_name configuration attribute adds the localhost name automatically. refs #5790 --- doc/09-object-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/09-object-types.md b/doc/09-object-types.md index 3922f92b8..2976356f0 100644 --- a/doc/09-object-types.md +++ b/doc/09-object-types.md @@ -446,7 +446,7 @@ You can create downtimes with the [schedule-downtime](12-icinga2-api.md#icinga2- Example: ``` -object Downtime "localhost!my-downtime" { +object Downtime "my-downtime" { host_name = "localhost" author = "icingaadmin" comment = "This is a downtime." From 6add03e29e830afd1659bdde011a327098dc4ddd Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Fri, 15 Dec 2017 07:41:05 +0100 Subject: [PATCH 17/35] Documentation: Remove redundant FreeBSD restart instructions refs #5866 --- doc/02-getting-started.md | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/doc/02-getting-started.md b/doc/02-getting-started.md index f39dac122..c1ae7cd0f 100644 --- a/doc/02-getting-started.md +++ b/doc/02-getting-started.md @@ -613,11 +613,7 @@ RHEL/CentOS 7/Fedora, SLES 12, Debian Jessie/Stretch, Ubuntu Xenial: # systemctl restart icinga2 -Debian/Ubuntu, RHEL/CentOS 6 and SUSE 11: - - # service icinga2 restart - -FreeBSD: +Debian/Ubuntu, RHEL/CentOS 6, SUSE 11 and FreeBSD: # service icinga2 restart @@ -766,11 +762,7 @@ RHEL/CentOS 7/Fedora, SLES 12, Debian Jessie/Stretch, Ubuntu Xenial: # systemctl restart icinga2 -Debian/Ubuntu, RHEL/CentOS 6, SUSE and FreeBSD: - - # service icinga2 restart - -FreeBSD: +Debian/Ubuntu, RHEL/CentOS 6, SUSE 11 and FreeBSD: # service icinga2 restart @@ -867,11 +859,7 @@ RHEL/CentOS 7/Fedora, SLES 12, Debian Jessie/Stretch, Ubuntu Xenial: # systemctl restart icinga2 -Debian/Ubuntu, RHEL/CentOS 6 and SUSE: - - # service icinga2 restart - -FreeBSD: +Debian/Ubuntu, RHEL/CentOS 6, SUSE 11 and FreeBSD: # service icinga2 restart From 05a0425a7cd86a07e2079a2cd940a37d847a9868 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Fri, 15 Dec 2017 08:19:00 +0100 Subject: [PATCH 18/35] Documentation: Add openSUSE to restart instructions refs #5866 --- doc/02-getting-started.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/02-getting-started.md b/doc/02-getting-started.md index c1ae7cd0f..dc00af57d 100644 --- a/doc/02-getting-started.md +++ b/doc/02-getting-started.md @@ -609,11 +609,11 @@ You can enable the `ido-mysql` feature configuration file using After enabling the ido-mysql feature you have to restart Icinga 2: -RHEL/CentOS 7/Fedora, SLES 12, Debian Jessie/Stretch, Ubuntu Xenial: +RHEL/CentOS 7/Fedora, SLES 12/openSUSE > 12.2, Debian Jessie/Stretch, Ubuntu Xenial: # systemctl restart icinga2 -Debian/Ubuntu, RHEL/CentOS 6, SUSE 11 and FreeBSD: +Debian/Ubuntu, RHEL/CentOS 6, SLES 11/openSUSE < 12.3 and FreeBSD: # service icinga2 restart @@ -758,11 +758,11 @@ You can enable the `ido-pgsql` feature configuration file using After enabling the ido-pgsql feature you have to restart Icinga 2: -RHEL/CentOS 7/Fedora, SLES 12, Debian Jessie/Stretch, Ubuntu Xenial: +RHEL/CentOS 7/Fedora, SLES 12/openSUSE > 12.2, Debian Jessie/Stretch, Ubuntu Xenial: # systemctl restart icinga2 -Debian/Ubuntu, RHEL/CentOS 6, SUSE 11 and FreeBSD: +Debian/Ubuntu, RHEL/CentOS 6, SLES 11/openSUSE < 12.3 and FreeBSD: # service icinga2 restart @@ -855,11 +855,11 @@ attribute with minimal permissions required by Icinga Web 2. Make sure to restart Icinga 2 to activate the configuration. -RHEL/CentOS 7/Fedora, SLES 12, Debian Jessie/Stretch, Ubuntu Xenial: +RHEL/CentOS 7/Fedora, SLES 12/openSUSE > 12.2, Debian Jessie/Stretch, Ubuntu Xenial: # systemctl restart icinga2 -Debian/Ubuntu, RHEL/CentOS 6, SUSE 11 and FreeBSD: +Debian/Ubuntu, RHEL/CentOS 6, SLES 11/openSUSE < 12.3 and FreeBSD: # service icinga2 restart From 36674db14fa8ed0d616740281a12103ae4043607 Mon Sep 17 00:00:00 2001 From: Noah Hilverling Date: Thu, 14 Dec 2017 17:08:03 +0100 Subject: [PATCH 19/35] Add missing initdb to PostgreSQL documentation refs #5864 --- doc/02-getting-started.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/02-getting-started.md b/doc/02-getting-started.md index dc00af57d..b9000a398 100644 --- a/doc/02-getting-started.md +++ b/doc/02-getting-started.md @@ -635,6 +635,7 @@ RHEL/CentOS 6: # yum install postgresql-server postgresql # chkconfig postgresql on + # service postgresql initdb # service postgresql start RHEL/CentOS 7: @@ -648,12 +649,14 @@ SUSE: # zypper install postgresql postgresql-server # chkconfig postgresql on + # service postgresql initdb # service postgresql start FreeBSD: # pkg install postgresql93-server # sysrc postgresql_enable=yes + # service postgresql initdb # service postgresql start Alpine Linux: From 1256fb3356e984c7da9b0eec29ce799a1a61ab8a Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Fri, 22 Dec 2017 09:53:56 +0100 Subject: [PATCH 20/35] Revert "Fix newline terminator for bulk requests in ElasticsearchWriter" This reverts commit 806eb0d2d2b077a9a4bac073bfbb27c2af369008. refs #5840 --- lib/perfdata/elasticsearchwriter.cpp | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/perfdata/elasticsearchwriter.cpp b/lib/perfdata/elasticsearchwriter.cpp index 2171752ca..69221fd88 100644 --- a/lib/perfdata/elasticsearchwriter.cpp +++ b/lib/perfdata/elasticsearchwriter.cpp @@ -395,11 +395,6 @@ void ElasticsearchWriter::Flush(void) String body = boost::algorithm::join(m_DataBuffer, "\n"); m_DataBuffer.clear(); - /* Elasticsearch 6.x requires a new line. This is compatible to 5.x. - * Tested with 6.0.0 and 5.6.4. - */ - body += "\n"; - SendRequest(body); } @@ -459,20 +454,12 @@ void ElasticsearchWriter::SendRequest(const String& body) try { resp.Parse(context, true); - while (resp.Parse(context, true) && !resp.Complete) - ; /* Do nothing */ } catch (const std::exception& ex) { Log(LogWarning, "ElasticsearchWriter") - << "Failed to parse HTTP response from host '" << GetHost() << "' port '" << GetPort() << "': " << DiagnosticInformation(ex, false); + << "Cannot read from HTTP API on host '" << GetHost() << "' port '" << GetPort() << "'."; throw ex; } - if (!resp.Complete) { - Log(LogWarning, "ElasticsearchWriter") - << "Failed to read a complete HTTP response from the Elasticsearch server."; - return; - } - if (resp.StatusCode > 299) { if (resp.StatusCode == 401) { /* More verbose error logging with Elasticsearch is hidden behind a proxy. */ @@ -491,6 +478,10 @@ void ElasticsearchWriter::SendRequest(const String& body) Log(LogWarning, "ElasticsearchWriter") << "Unexpected response code " << resp.StatusCode; + /* Finish parsing the headers and body. */ + while (!resp.Complete) + resp.Parse(context, true); + String contentType = resp.Headers->Get("content-type"); if (contentType != "application/json") { @@ -517,8 +508,6 @@ void ElasticsearchWriter::SendRequest(const String& body) Log(LogCritical, "ElasticsearchWriter") << "Elasticsearch error message:\n" << error; - - return; } } From 4b7507cfcf945e711b1d44cf7be1616cbeccf819 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Fri, 22 Dec 2017 09:54:20 +0100 Subject: [PATCH 21/35] Revert "Update docs for Elasticsearch 5.x and 6.x support" This reverts commit 9b0cc125dd9d893a7e45547a252aacd03cea845c. refs #5840 --- doc/14-features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/14-features.md b/doc/14-features.md index 316707a3e..e2fdeb591 100644 --- a/doc/14-features.md +++ b/doc/14-features.md @@ -347,7 +347,7 @@ The check results include parsed performance data metrics if enabled. > **Note** > -> Elasticsearch 5.x+ is required. This feature has been tested with Elasticsearch 5.6.4 and 6.0.0. +> Elasticsearch 5.x+ is required. Enable the feature and restart Icinga 2. From a6f32d78c3a51b23495440ed9ba89c39f6a9f080 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Fri, 22 Dec 2017 10:11:37 +0100 Subject: [PATCH 22/35] Update ElasticsearchWriter docs for 5.x support only refs #5911 --- doc/14-features.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/14-features.md b/doc/14-features.md index e2fdeb591..9f4db6a78 100644 --- a/doc/14-features.md +++ b/doc/14-features.md @@ -347,7 +347,7 @@ The check results include parsed performance data metrics if enabled. > **Note** > -> Elasticsearch 5.x+ is required. +> Elasticsearch 5.x is required. This feature has been successfully tested with Elasticsearch 5.6.4. Enable the feature and restart Icinga 2. From 74d5137dde05a1d15acdf88be2059661073cb4c5 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 13 Dec 2017 22:31:56 +0100 Subject: [PATCH 23/35] Implement AppLocal deployment support for UCRT refs #5856 --- CMakeLists.txt | 6 +++--- icinga-installer/icinga2.wixpatch.cmake | 13 ------------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 52c698bb0..761feb5c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -318,7 +318,8 @@ set(CPACK_WIX_UI_DIALOG "${CMAKE_CURRENT_SOURCE_DIR}/icinga-installer/dlgbmp.bmp set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_BINARY_DIR}/icinga-installer/icinga2.wixpatch.Debug") set(CPACK_WIX_PATCH_FILE "${CMAKE_CURRENT_BINARY_DIR}/icinga-installer/icinga2.wixpatch") -set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) +set(CMAKE_INSTALL_SYSTEM_RUNTIME_DESTINATION "sbin") +set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE) include(InstallRequiredSystemLibraries) if(WIN32) @@ -352,8 +353,7 @@ if(WIN32) endif() install( - PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} - ${ICINGA2_OPENSSL_DLLS} + PROGRAMS ${ICINGA2_OPENSSL_DLLS} DESTINATION ${CMAKE_INSTALL_SBINDIR} ) endif() diff --git a/icinga-installer/icinga2.wixpatch.cmake b/icinga-installer/icinga2.wixpatch.cmake index 099d940c4..0666261d2 100644 --- a/icinga-installer/icinga2.wixpatch.cmake +++ b/icinga-installer/icinga2.wixpatch.cmake @@ -10,9 +10,6 @@ - - - $CM_CP_sbin.icinga2_installer.exe>2 $CM_CP_sbin.icinga2_installer.exe>2 $CM_CP_sbin.icinga2_installer.exe=2 @@ -32,15 +29,5 @@ Event="DoAction" Value="LaunchIcinga2Wizard">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed - - - - - - - - From 2901c77d65a1cce0c4ef298b0a87b0658217e772 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 16 Jan 2018 09:49:46 +0100 Subject: [PATCH 24/35] Fix wrong schema constraint for fresh 2.8.0 installations This fix is only needed for a fresh 2.8.0 setup, older versions and upgrades to current do not need this (can be applied as idempotent update). fixes #5947 refs #5986 --- doc/16-upgrading-icinga-2.md | 5 ++ lib/db_ido_mysql/schema/mysql.sql | 12 ++-- lib/db_ido_mysql/schema/upgrade/2.8.1.sql | 67 +++++++++++++++++++++++ lib/db_ido_pgsql/schema/pgsql.sql | 12 ++-- lib/db_ido_pgsql/schema/upgrade/2.8.1.sql | 19 +++++++ 5 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 lib/db_ido_mysql/schema/upgrade/2.8.1.sql create mode 100644 lib/db_ido_pgsql/schema/upgrade/2.8.1.sql diff --git a/doc/16-upgrading-icinga-2.md b/doc/16-upgrading-icinga-2.md index 56dce46e4..b5a7f0500 100644 --- a/doc/16-upgrading-icinga-2.md +++ b/doc/16-upgrading-icinga-2.md @@ -15,6 +15,11 @@ There are additional indexes and schema fixes which require an update. Please proceed here for [MySQL](16-upgrading-icinga-2.md#upgrading-mysql-db) or [PostgreSQL](16-upgrading-icinga-2.md#upgrading-postgresql-db). +> **Note** +> +> `2.8.1.sql` fixes a unique constraint problem with fresh 2.8.0 installations. +> You don't need this update if you are upgrading from an older version. + ### Changed Certificate Paths The default certificate path was changed from `/etc/icinga2/pki` to diff --git a/lib/db_ido_mysql/schema/mysql.sql b/lib/db_ido_mysql/schema/mysql.sql index 2acff559a..ccaf42054 100644 --- a/lib/db_ido_mysql/schema/mysql.sql +++ b/lib/db_ido_mysql/schema/mysql.sql @@ -80,8 +80,7 @@ CREATE TABLE IF NOT EXISTS icinga_commenthistory ( deletion_time timestamp NULL, deletion_time_usec int default 0, name TEXT character set latin1 default NULL, - PRIMARY KEY (commenthistory_id), - UNIQUE KEY instance_id (instance_id,object_id,comment_time,internal_comment_id) + PRIMARY KEY (commenthistory_id) ) ENGINE=InnoDB COMMENT='Historical host and service comments'; -- -------------------------------------------------------- @@ -108,8 +107,7 @@ CREATE TABLE IF NOT EXISTS icinga_comments ( expiration_time timestamp NULL, name TEXT character set latin1 default NULL, session_token int default NULL, - PRIMARY KEY (comment_id), - UNIQUE KEY instance_id (instance_id,object_id,comment_time,internal_comment_id) + PRIMARY KEY (comment_id) ) ENGINE=InnoDB COMMENT='Usercomments on Icinga objects'; -- -------------------------------------------------------- @@ -412,8 +410,7 @@ CREATE TABLE IF NOT EXISTS icinga_downtimehistory ( is_in_effect smallint default 0, trigger_time timestamp NULL, name TEXT character set latin1 default NULL, - PRIMARY KEY (downtimehistory_id), - UNIQUE KEY instance_id (instance_id,object_id,entry_time,internal_downtime_id) + PRIMARY KEY (downtimehistory_id) ) ENGINE=InnoDB COMMENT='Historical scheduled host and service downtime'; -- -------------------------------------------------------- @@ -977,8 +974,7 @@ CREATE TABLE IF NOT EXISTS icinga_scheduleddowntime ( trigger_time timestamp NULL, name TEXT character set latin1 default NULL, session_token int default NULL, - PRIMARY KEY (scheduleddowntime_id), - UNIQUE KEY instance_id (instance_id,object_id,entry_time,internal_downtime_id) + PRIMARY KEY (scheduleddowntime_id) ) ENGINE=InnoDB COMMENT='Current scheduled host and service downtime'; -- -------------------------------------------------------- diff --git a/lib/db_ido_mysql/schema/upgrade/2.8.1.sql b/lib/db_ido_mysql/schema/upgrade/2.8.1.sql new file mode 100644 index 000000000..4bf943e10 --- /dev/null +++ b/lib/db_ido_mysql/schema/upgrade/2.8.1.sql @@ -0,0 +1,67 @@ +-- ----------------------------------------- +-- upgrade path for Icinga 2.8.1 (fix for fresh 2.8.0 installation only) +-- +-- ----------------------------------------- +-- Copyright (c) 2018 Icinga Development Team (https://www.icinga.com) +-- +-- Please check https://docs.icinga.com for upgrading information! +-- ----------------------------------------- + +SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; + +-- -------------------------------------------------------- +-- Helper functions and procedures for DROP INDEX IF EXISTS +-- -------------------------------------------------------- + +DELIMITER // +DROP FUNCTION IF EXISTS ido_index_exists // +CREATE FUNCTION ido_index_exists( + f_table_name varchar(64), + f_index_name varchar(64) +) + RETURNS BOOL + DETERMINISTIC + READS SQL DATA + BEGIN + DECLARE index_exists BOOL DEFAULT FALSE; + SELECT EXISTS ( + SELECT 1 + FROM information_schema.statistics + WHERE table_schema = SCHEMA() + AND table_name = f_table_name + AND index_name = f_index_name + ) INTO index_exists; + RETURN index_exists; + END // + +DROP PROCEDURE IF EXISTS ido_drop_index_if_exists // +CREATE PROCEDURE ido_drop_index_if_exists ( + IN p_table_name varchar(64), + IN p_index_name varchar(64) +) + DETERMINISTIC + MODIFIES SQL DATA + BEGIN + IF ido_index_exists(p_table_name, p_index_name) + THEN + SET @ido_drop_index_sql = CONCAT('ALTER TABLE `', SCHEMA(), '`.`', p_table_name, '` DROP INDEX `', p_index_name, '`'); + PREPARE stmt FROM @ido_drop_index_sql; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + SET @ido_drop_index_sql = NULL; + END IF; + END // +DELIMITER ; + +CALL ido_drop_index_if_exists('icinga_downtimehistory', 'instance_id'); +CALL ido_drop_index_if_exists('icinga_scheduleddowntime', 'instance_id'); +CALL ido_drop_index_if_exists('icinga_commenthistory', 'instance_id'); +CALL ido_drop_index_if_exists('icinga_comments', 'instance_id'); + +DROP FUNCTION ido_index_exists; +DROP PROCEDURE ido_drop_index_if_exists; + +-- ----------------------------------------- +-- set dbversion (same as 2.8.0) +-- ----------------------------------------- +INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.14.3', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.14.3', modify_time=NOW(); diff --git a/lib/db_ido_pgsql/schema/pgsql.sql b/lib/db_ido_pgsql/schema/pgsql.sql index fb033d2b7..8c4e5e919 100644 --- a/lib/db_ido_pgsql/schema/pgsql.sql +++ b/lib/db_ido_pgsql/schema/pgsql.sql @@ -112,8 +112,7 @@ CREATE TABLE icinga_commenthistory ( deletion_time timestamp, deletion_time_usec INTEGER default 0, name TEXT default NULL, - CONSTRAINT PK_commenthistory_id PRIMARY KEY (commenthistory_id) , - CONSTRAINT UQ_commenthistory UNIQUE (instance_id,object_id,comment_time,internal_comment_id) + CONSTRAINT PK_commenthistory_id PRIMARY KEY (commenthistory_id) ); -- -------------------------------------------------------- @@ -140,8 +139,7 @@ CREATE TABLE icinga_comments ( expiration_time timestamp, name TEXT default NULL, session_token INTEGER default NULL, - CONSTRAINT PK_comment_id PRIMARY KEY (comment_id) , - CONSTRAINT UQ_comments UNIQUE (instance_id,object_id,comment_time,internal_comment_id) + CONSTRAINT PK_comment_id PRIMARY KEY (comment_id) ) ; -- -------------------------------------------------------- @@ -448,8 +446,7 @@ CREATE TABLE icinga_downtimehistory ( is_in_effect INTEGER default 0, trigger_time timestamp, name TEXT default NULL, - CONSTRAINT PK_downtimehistory_id PRIMARY KEY (downtimehistory_id) , - CONSTRAINT UQ_downtimehistory UNIQUE (instance_id,object_id,entry_time,internal_downtime_id) + CONSTRAINT PK_downtimehistory_id PRIMARY KEY (downtimehistory_id) ) ; -- -------------------------------------------------------- @@ -1014,8 +1011,7 @@ CREATE TABLE icinga_scheduleddowntime ( trigger_time timestamp, name TEXT default NULL, session_token INTEGER default NULL, - CONSTRAINT PK_scheduleddowntime_id PRIMARY KEY (scheduleddowntime_id) , - CONSTRAINT UQ_scheduleddowntime UNIQUE (instance_id,object_id,entry_time,internal_downtime_id) + CONSTRAINT PK_scheduleddowntime_id PRIMARY KEY (scheduleddowntime_id) ) ; -- -------------------------------------------------------- diff --git a/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql b/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql new file mode 100644 index 000000000..0ee92f659 --- /dev/null +++ b/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql @@ -0,0 +1,19 @@ +-- ----------------------------------------- +-- upgrade path for Icinga 2.8.1 (fix for fresh 2.8.0 installation only) +-- +-- ----------------------------------------- +-- Copyright (c) 2018 Icinga Development Team (https://www.icinga.com) +-- +-- Please check https://docs.icinga.com for upgrading information! +-- ----------------------------------------- + +ALTER TABLE icinga_downtimehistory DROP CONSTRAINT IF EXISTS UQ_downtimehistory; +ALTER TABLE icinga_scheduleddowntime DROP CONSTRAINT IF EXISTS UQ_scheduleddowntime; +ALTER TABLE icinga_commenthistory DROP CONSTRAINT IF EXISTS UQ_commenthistory; +ALTER TABLE icinga_comments DROP CONSTRAINT IF EXISTS UQ_comments; + +-- ----------------------------------------- +-- set dbversion (same as 2.8.0) +-- ----------------------------------------- + +SELECT updatedbversion('1.14.3'); From fba838c7efc319eabe343c8914b86560ccafcff0 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 16 Jan 2018 10:41:48 +0100 Subject: [PATCH 25/35] Revert "Make default getters and setters non-virtual" This reverts commit 50eb774a0f57c8a09c0583e2be204bcc5bfd58cf. refs #5759 --- tools/mkclass/class_lexer.ll | 3 --- tools/mkclass/classcompiler.cpp | 25 +++++++------------------ tools/mkclass/classcompiler.hpp | 4 +--- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/tools/mkclass/class_lexer.ll b/tools/mkclass/class_lexer.ll index 625a99f22..0b9195464 100644 --- a/tools/mkclass/class_lexer.ll +++ b/tools/mkclass/class_lexer.ll @@ -148,9 +148,6 @@ no_storage { yylval->num = FANoStorage; return T_FIELD_ATTRIBUTE; } no_user_modify { yylval->num = FANoUserModify; return T_FIELD_ATTRIBUTE; } no_user_view { yylval->num = FANoUserView; return T_FIELD_ATTRIBUTE; } deprecated { yylval->num = FADeprecated; return T_FIELD_ATTRIBUTE; } -get_virtual { yylval->num = FAGetVirtual; return T_FIELD_ATTRIBUTE; } -set_virtual { yylval->num = FASetVirtual; return T_FIELD_ATTRIBUTE; } -virtual { yylval->num = FAGetVirtual | FASetVirtual; return T_FIELD_ATTRIBUTE; } navigation { return T_NAVIGATION; } validator { return T_VALIDATOR; } required { return T_REQUIRED; } diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index 7d068bcd5..04e9abdeb 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -706,7 +706,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) /* NotifyField */ m_Header << "public:" << std::endl - << "\t" << "void NotifyField(int id, const Value& cookie = Empty) override;" << std::endl; + << "\t" << "virtual void NotifyField(int id, const Value& cookie = Empty) override;" << std::endl; m_Impl << "void ObjectImpl<" << klass.Name << ">::NotifyField(int id, const Value& cookie)" << std::endl << "{" << std::endl; @@ -784,12 +784,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) prot = "public"; m_Header << prot << ":" << std::endl - << "\t"; - - if (field.Attributes & FAGetVirtual || field.PureGetAccessor) - m_Header << "virtual "; - - m_Header << field.Type.GetRealType() << " Get" << field.GetFriendlyName() << "(void) const"; + << "\t" << "virtual " << field.Type.GetRealType() << " Get" << field.GetFriendlyName() << "(void) const"; if (field.PureGetAccessor) { m_Header << " = 0;" << std::endl; @@ -818,12 +813,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) prot = "public"; m_Header << prot << ":" << std::endl - << "\t"; - - if (field.Attributes & FASetVirtual || field.PureSetAccessor) - m_Header << "virtual "; - - m_Header << "void Set" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " value, bool suppress_events = false, const Value& cookie = Empty)"; + << "\t" << "virtual void Set" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " value, bool suppress_events = false, const Value& cookie = Empty)"; if (field.PureSetAccessor) { m_Header << " = 0;" << std::endl; @@ -869,7 +859,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) needs_tracking = true; - m_Header << "\t" << "void Track" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " oldValue, " << field.Type.GetArgumentType() << " newValue);" << std::endl; + m_Header << "\t" << "virtual void Track" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " oldValue, " << field.Type.GetArgumentType() << " newValue);"; m_Impl << "void ObjectImpl<" << klass.Name << ">::Track" << field.GetFriendlyName() << "(" << field.Type.GetArgumentType() << " oldValue, " << field.Type.GetArgumentType() << " newValue)" << std::endl << "{" << std::endl; @@ -940,7 +930,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) continue; m_Header << "public:" << std::endl - << "\t" << "Object::Ptr Navigate" << field.GetFriendlyName() << "(void) const"; + << "\t" << "virtual Object::Ptr Navigate" << field.GetFriendlyName() << "(void) const"; if (field.PureNavigateAccessor) { m_Header << " = 0;" << std::endl; @@ -961,9 +951,8 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) /* start/stop */ if (needs_tracking) { - m_Header << "protected:" << std::endl - << "\tvirtual void Start(bool runtimeCreated = false) override;" << std::endl - << "\tvirtual void Stop(bool runtimeRemoved = false) override;" << std::endl; + m_Header << "virtual void Start(bool runtimeCreated = false) override;" << std::endl + << "virtual void Stop(bool runtimeRemoved = false) override;" << std::endl; m_Impl << "void ObjectImpl<" << klass.Name << ">::Start(bool runtimeCreated)" << std::endl << "{" << std::endl diff --git a/tools/mkclass/classcompiler.hpp b/tools/mkclass/classcompiler.hpp index ab9422220..06bf100d3 100644 --- a/tools/mkclass/classcompiler.hpp +++ b/tools/mkclass/classcompiler.hpp @@ -73,9 +73,7 @@ enum FieldAttribute FANavigation = 512, FANoUserModify = 1024, FANoUserView = 2048, - FADeprecated = 4096, - FAGetVirtual = 8192, - FASetVirtual = 16384 + FADeprecated = 4096 }; struct FieldType From c3fe1a63232a5a5de119d2458939edd52d3958a5 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 16 Jan 2018 10:43:16 +0100 Subject: [PATCH 26/35] Revert "Fix incorrect socket handling for the HTTP client" This reverts commit 59da943548c7c1be5f4b27dce73a0ad27cf0de5d. refs #5760 --- lib/base/stream.cpp | 3 --- lib/remote/httpresponse.cpp | 39 ++++++++++++++----------------------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/lib/base/stream.cpp b/lib/base/stream.cpp index 57791d305..9390d97b9 100644 --- a/lib/base/stream.cpp +++ b/lib/base/stream.cpp @@ -145,9 +145,6 @@ bool StreamReadContext::FillFromStream(const Stream::Ptr& stream, bool may_wait) if (!Buffer) throw std::bad_alloc(); - if (stream->IsEof()) - break; - size_t rc = stream->Read(Buffer + Size, 4096, true); Size += rc; diff --git a/lib/remote/httpresponse.cpp b/lib/remote/httpresponse.cpp index ab7458655..7f77ed386 100644 --- a/lib/remote/httpresponse.cpp +++ b/lib/remote/httpresponse.cpp @@ -159,7 +159,12 @@ bool HttpResponse::Parse(StreamReadContext& src, bool may_wait) if (line == "") { m_State = HttpResponseBody; - m_Body = new FIFO(); + + /* we're done if the request doesn't contain a message body */ + if (!Headers->Contains("content-length") && !Headers->Contains("transfer-encoding")) + Complete = true; + else + m_Body = new FIFO(); return true; @@ -199,41 +204,27 @@ bool HttpResponse::Parse(StreamReadContext& src, bool may_wait) return true; } } else { - bool hasLengthIndicator = false; - size_t lengthIndicator = 0; - Value contentLengthHeader; - - if (Headers->Get("content-length", &contentLengthHeader)) { - hasLengthIndicator = true; - lengthIndicator = Convert::ToLong(contentLengthHeader); - } - - if (hasLengthIndicator && src.Eof) + if (src.Eof) BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); if (src.MustRead) { - if (!src.FillFromStream(m_Stream, may_wait)) + if (!src.FillFromStream(m_Stream, false)) { src.Eof = true; + BOOST_THROW_EXCEPTION(std::invalid_argument("Unexpected EOF in HTTP body")); + } src.MustRead = false; } - if (!hasLengthIndicator) - lengthIndicator = src.Size; + size_t length_indicator = Convert::ToLong(Headers->Get("content-length")); - if (src.Size < lengthIndicator) { + if (src.Size < length_indicator) { src.MustRead = true; - return may_wait; - } - - m_Body->Write(src.Buffer, lengthIndicator); - src.DropData(lengthIndicator); - - if (!hasLengthIndicator && !src.Eof) { - src.MustRead = true; - return may_wait; + return false; } + m_Body->Write(src.Buffer, length_indicator); + src.DropData(length_indicator); Complete = true; return true; } From b4a049523917bbdef1af76cd62a3a817f4e56517 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 16 Jan 2018 10:45:20 +0100 Subject: [PATCH 27/35] Revert "Fix performance issues for InfluxdbWriter" This reverts commit eea451ee94be7310e63ab6b7e5e94924b295e974. refs #5764 --- lib/perfdata/influxdbwriter.cpp | 182 +++++++++++++++++--------------- lib/perfdata/influxdbwriter.hpp | 12 ++- 2 files changed, 104 insertions(+), 90 deletions(-) diff --git a/lib/perfdata/influxdbwriter.cpp b/lib/perfdata/influxdbwriter.cpp index 499e77a89..aa35fec0e 100644 --- a/lib/perfdata/influxdbwriter.cpp +++ b/lib/perfdata/influxdbwriter.cpp @@ -49,24 +49,6 @@ using namespace icinga; -class InfluxdbInteger : public Object -{ -public: - DECLARE_PTR_TYPEDEFS(InfluxdbInteger); - - InfluxdbInteger(int value) - : m_Value(value) - { } - - int GetValue(void) const - { - return m_Value; - } - -private: - int m_Value; -}; - REGISTER_TYPE(InfluxdbWriter); REGISTER_STATSFUNCTION(InfluxdbWriter, &InfluxdbWriter::StatsFunc); @@ -124,7 +106,7 @@ void InfluxdbWriter::Start(bool runtimeCreated) m_FlushTimer->Reschedule(0); /* Register for new metrics. */ - Checkable::OnNewCheckResult.connect(boost::bind(&InfluxdbWriter::CheckResultHandler, this, _1, _2)); + Service::OnNewCheckResult.connect(boost::bind(&InfluxdbWriter::CheckResultHandler, this, _1, _2)); } void InfluxdbWriter::Stop(bool runtimeRemoved) @@ -152,7 +134,7 @@ void InfluxdbWriter::ExceptionHandler(boost::exception_ptr exp) //TODO: Close the connection, if we keep it open. } -Stream::Ptr InfluxdbWriter::Connect(void) +Stream::Ptr InfluxdbWriter::Connect() { TcpSocket::Ptr socket = new TcpSocket(); @@ -194,10 +176,10 @@ Stream::Ptr InfluxdbWriter::Connect(void) void InfluxdbWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { - m_WorkQueue.Enqueue(boost::bind(&InfluxdbWriter::CheckResultHandlerWQ, this, checkable, cr), PriorityLow); + m_WorkQueue.Enqueue(boost::bind(&InfluxdbWriter::InternalCheckResultHandler, this, checkable, cr)); } -void InfluxdbWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) +void InfluxdbWriter::InternalCheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr) { AssertOnWorkQueue(); @@ -229,16 +211,28 @@ void InfluxdbWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable, const if (tags) { ObjectLock olock(tags); for (const Dictionary::Pair& pair : tags) { + // Prevent missing macros from warning; will return an empty value + // which will be filtered out in SendMetric() String missing_macro; - Value value = MacroProcessor::ResolveMacros(pair.second, resolvers, cr, &missing_macro); - - if (!missing_macro.IsEmpty()) - continue; - - tags->Set(pair.first, value); + tags->Set(pair.first, MacroProcessor::ResolveMacros(pair.second, resolvers, cr, &missing_macro)); } } + SendPerfdata(tmpl, checkable, cr, ts); +} + +String InfluxdbWriter::FormatInteger(int val) +{ + return Convert::ToString(val) + "i"; +} + +String InfluxdbWriter::FormatBoolean(bool val) +{ + return val ? "true" : "false"; +} + +void InfluxdbWriter::SendPerfdata(const Dictionary::Ptr& tmpl, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, double ts) +{ Array::Ptr perfdata = cr->GetPerformanceData(); if (perfdata) { ObjectLock olock(perfdata); @@ -283,16 +277,16 @@ void InfluxdbWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable, const Dictionary::Ptr fields = new Dictionary(); if (service) - fields->Set("state", new InfluxdbInteger(service->GetState())); + fields->Set("state", FormatInteger(service->GetState())); else - fields->Set("state", new InfluxdbInteger(host->GetState())); + fields->Set("state", FormatInteger(host->GetState())); - fields->Set("current_attempt", new InfluxdbInteger(checkable->GetCheckAttempt())); - fields->Set("max_check_attempts", new InfluxdbInteger(checkable->GetMaxCheckAttempts())); - fields->Set("state_type", new InfluxdbInteger(checkable->GetStateType())); - fields->Set("reachable", checkable->IsReachable()); - fields->Set("downtime_depth", new InfluxdbInteger(checkable->GetDowntimeDepth())); - fields->Set("acknowledgement", new InfluxdbInteger(checkable->GetAcknowledgement())); + fields->Set("current_attempt", FormatInteger(checkable->GetCheckAttempt())); + fields->Set("max_check_attempts", FormatInteger(checkable->GetMaxCheckAttempts())); + fields->Set("state_type", FormatInteger(checkable->GetStateType())); + fields->Set("reachable", FormatBoolean(checkable->IsReachable())); + fields->Set("downtime_depth", FormatInteger(checkable->GetDowntimeDepth())); + fields->Set("acknowledgement", FormatInteger(checkable->GetAcknowledgement())); fields->Set("latency", cr->CalculateLatency()); fields->Set("execution_time", cr->CalculateExecutionTime()); @@ -300,7 +294,7 @@ void InfluxdbWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable, const } } -String InfluxdbWriter::EscapeKeyOrTagValue(const String& str) +String InfluxdbWriter::EscapeKey(const String& str) { // Iterate over the key name and escape commas and spaces with a backslash String result = str; @@ -308,28 +302,57 @@ String InfluxdbWriter::EscapeKeyOrTagValue(const String& str) boost::algorithm::replace_all(result, "=", "\\="); boost::algorithm::replace_all(result, ",", "\\,"); boost::algorithm::replace_all(result, " ", "\\ "); + + // InfluxDB 'feature': although backslashes are allowed in keys they also act + // as escape sequences when followed by ',' or ' '. When your tag is like + // 'metric=C:\' bad things happen. Backslashes themselves cannot be escaped + // and through experimentation they also escape '='. To be safe we replace + // trailing backslashes with and underscore. + size_t length = result.GetLength(); + if (result[length - 1] == '\\') + result[length - 1] = '_'; + return result; } -String InfluxdbWriter::EscapeValue(const Value& value) +String InfluxdbWriter::EscapeField(const String& str) { - if (value.IsObjectType()) { - std::ostringstream os; - os << static_cast(value)->GetValue() - << "i"; - return os.str(); + //TODO: Evaluate whether boost::regex is really needed here. + + // Handle integers + boost::regex integer("-?\\d+i"); + if (boost::regex_match(str.GetData(), integer)) { + return str; } - if (value.IsBoolean()) - return value ? "true" : "false"; + // Handle numerics + boost::regex numeric("-?\\d+(\\.\\d+)?((e|E)[+-]?\\d+)?"); + if (boost::regex_match(str.GetData(), numeric)) { + return str; + } - return value; + // Handle booleans + boost::regex boolean_true("t|true", boost::regex::icase); + if (boost::regex_match(str.GetData(), boolean_true)) + return "true"; + boost::regex boolean_false("f|false", boost::regex::icase); + if (boost::regex_match(str.GetData(), boolean_false)) + return "false"; + + // Handle NaNs + if (boost::math::isnan(str)) + return 0; + + // Otherwise it's a string and needs escaping and quoting + String result = str; + boost::algorithm::replace_all(result, "\"", "\\\""); + return "\"" + result + "\""; } void InfluxdbWriter::SendMetric(const Dictionary::Ptr& tmpl, const String& label, const Dictionary::Ptr& fields, double ts) { std::ostringstream msgbuf; - msgbuf << EscapeKeyOrTagValue(tmpl->Get("measurement")); + msgbuf << EscapeKey(tmpl->Get("measurement")); Dictionary::Ptr tags = tmpl->Get("tags"); if (tags) { @@ -337,14 +360,14 @@ void InfluxdbWriter::SendMetric(const Dictionary::Ptr& tmpl, const String& label for (const Dictionary::Pair& pair : tags) { // Empty macro expansion, no tag if (!pair.second.IsEmpty()) { - msgbuf << "," << EscapeKeyOrTagValue(pair.first) << "=" << EscapeKeyOrTagValue(pair.second); + msgbuf << "," << EscapeKey(pair.first) << "=" << EscapeKey(pair.second); } } } - // Label may be empty in the case of metadata + // Label is may be empty in the case of metadata if (!label.IsEmpty()) - msgbuf << ",metric=" << EscapeKeyOrTagValue(label); + msgbuf << ",metric=" << EscapeKey(label); msgbuf << " "; @@ -358,54 +381,45 @@ void InfluxdbWriter::SendMetric(const Dictionary::Ptr& tmpl, const String& label else msgbuf << ","; - msgbuf << EscapeKeyOrTagValue(pair.first) << "=" << EscapeValue(pair.second); + msgbuf << EscapeKey(pair.first) << "=" << EscapeField(pair.second); } } msgbuf << " " << static_cast(ts); -#ifdef I2_DEBUG Log(LogDebug, "InfluxdbWriter") << "Add to metric list: '" << msgbuf.str() << "'."; -#endif /* I2_DEBUG */ - // Buffer the data point - m_DataBuffer.push_back(msgbuf.str()); + // Atomically buffer the data point + boost::mutex::scoped_lock lock(m_DataBufferMutex); + m_DataBuffer.push_back(String(msgbuf.str())); // Flush if we've buffered too much to prevent excessive memory use if (static_cast(m_DataBuffer.size()) >= GetFlushThreshold()) { Log(LogDebug, "InfluxdbWriter") << "Data buffer overflow writing " << m_DataBuffer.size() << " data points"; - - try { - Flush(); - } catch (...) { - /* Do nothing. */ - } + Flush(); } } void InfluxdbWriter::FlushTimeout(void) { - m_WorkQueue.Enqueue(boost::bind(&InfluxdbWriter::FlushTimeoutWQ, this), PriorityHigh); -} - -void InfluxdbWriter::FlushTimeoutWQ(void) -{ - AssertOnWorkQueue(); + // Prevent new data points from being added to the array, there is a + // race condition where they could disappear + boost::mutex::scoped_lock lock(m_DataBufferMutex); // Flush if there are any data available - if (m_DataBuffer.empty()) - return; - - Log(LogDebug, "InfluxdbWriter") - << "Timer expired writing " << m_DataBuffer.size() << " data points"; - - Flush(); + if (m_DataBuffer.size() > 0) { + Log(LogDebug, "InfluxdbWriter") + << "Timer expired writing " << m_DataBuffer.size() << " data points"; + Flush(); + } } void InfluxdbWriter::Flush(void) { + // Ensure you hold a lock against m_DataBuffer so that things + // don't go missing after creating the body and clearing the buffer String body = boost::algorithm::join(m_DataBuffer, "\n"); m_DataBuffer.clear(); @@ -443,27 +457,25 @@ void InfluxdbWriter::Flush(void) throw ex; } + //TODO: Evaluate whether waiting for the result makes sense here. KeepAlive and close are options. HttpResponse resp(stream, req); StreamReadContext context; try { - while (resp.Parse(context, true) && !resp.Complete) - ; /* Do nothing */ + resp.Parse(context, true); } catch (const std::exception& ex) { Log(LogWarning, "InfluxdbWriter") - << "Failed to parse HTTP response from host '" << GetHost() << "' port '" << GetPort() << "': " << DiagnosticInformation(ex); + << "Cannot read from TCP socket from host '" << GetHost() << "' port '" << GetPort() << "'."; throw ex; } - if (!resp.Complete) { - Log(LogWarning, "InfluxdbWriter") - << "Failed to read a complete HTTP response from the InfluxDB server."; - return; - } - if (resp.StatusCode != 204) { Log(LogWarning, "InfluxdbWriter") - << "Unexpected response code: " << resp.StatusCode; + << "Unexpected response code " << resp.StatusCode; + + // Finish parsing the headers and body + while (!resp.Complete) + resp.Parse(context, true); String contentType = resp.Headers->Get("content-type"); if (contentType != "application/json") { @@ -490,8 +502,6 @@ void InfluxdbWriter::Flush(void) Log(LogCritical, "InfluxdbWriter") << "InfluxDB error message:\n" << error; - - return; } } diff --git a/lib/perfdata/influxdbwriter.hpp b/lib/perfdata/influxdbwriter.hpp index 97546bc84..e0a149c3a 100644 --- a/lib/perfdata/influxdbwriter.hpp +++ b/lib/perfdata/influxdbwriter.hpp @@ -59,16 +59,20 @@ private: WorkQueue m_WorkQueue; Timer::Ptr m_FlushTimer; std::vector m_DataBuffer; + boost::mutex m_DataBufferMutex; void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); - void CheckResultHandlerWQ(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); + void InternalCheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr); + void SendPerfdata(const Dictionary::Ptr& tmpl, const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, double ts); void SendMetric(const Dictionary::Ptr& tmpl, const String& label, const Dictionary::Ptr& fields, double ts); void FlushTimeout(void); - void FlushTimeoutWQ(void); void Flush(void); - static String EscapeKeyOrTagValue(const String& str); - static String EscapeValue(const Value& value); + static String FormatInteger(int val); + static String FormatBoolean(bool val); + + static String EscapeKey(const String& str); + static String EscapeField(const String& str); Stream::Ptr Connect(); From dde2a527b0b84dd10ac4b7ac2692ba67e04f367f Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 16 Jan 2018 15:49:53 +0100 Subject: [PATCH 28/35] changelog.py: Adjust categories and labels: Enhancement, Bug, ITL, Documentation, Support This PR allows to adjust the categories and matching labels. If no label matches, "Support" is the old hardcoded default. Issues with the "Documentation" and "ITL" label will be put into their respective category at the bottom before "Support". This increases readability and allows users to focus on the core vs additional config and docs. "Support" also applies to labels such as "code-quality", "Tests" and "Packages"/"Installation". Note: Labels are case sensitive. The order of the categories dictionary is important too. Since issues and PRs are the same for GitHub, and we don't require an issue for a PR anymore, we sometimes have duplicates. This patch adds an inline label called "PR" to highlight these PRs in the Changelog. refs #5989 --- changelog.py | 53 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/changelog.py b/changelog.py index 7b0da34ad..a6bf2afbc 100755 --- a/changelog.py +++ b/changelog.py @@ -8,6 +8,10 @@ import sys import os from datetime import datetime from collections import defaultdict +from collections import OrderedDict + +################################# +## Env Config try: github_auth_username = os.environ['ICINGA_GITHUB_AUTH_USERNAME'] @@ -27,10 +31,34 @@ except: print "ERROR: Environment variable 'ICINGA_GITHUB_PROJECT' is not set." sys.exit(1) +################################# +## Config + changelog_file = "CHANGELOG.md" # TODO: config param debug = 1 -ignored_labels = ["high", "low", "bug", "enhancement", "feedback", "question", "backported"] +# Keep this in sync with GitHub labels. +ignored_labels = [ + "high-priority", "low-priority", + "bug", "enhancement", + "needs-feedback", "question", "duplicate", "invalid", "wontfix", + "backported", "build-fix" +] + +# Selectively show and collect specific categories +# +# (category, list of case sensitive matching labels) +# The order is important! +# Keep this in sync with GitHub labels. +categories = OrderedDict( +[ + ("Enhancement", ["enhancement"]), + ("Bug", ["bug", "crash"]), + ("ITL", ["ITL"]), + ("Documentation", ["Documentation"]), + ("Support", ["code-quality", "Tests", "Packages", "Installation"]) +] +) ################################# ## Helpers @@ -74,12 +102,17 @@ def fetch_github_resources(uri, params = {}): return resources def issue_type(issue): - if "bug" in [label["name"] for label in issue["labels"]]: - return "Bug" - elif "enhancement" in [label["name"] for label in issue["labels"]]: - return "Enhancement" - else: - return "Support" + issue_labels = [label["name"] for label in issue["labels"]] + + # start with the least important first (e.g. "Support", "Documentation", "Bug", "Enhancement" as order) + for category in reversed(categories): + labels = categories[category] + + for label in labels: + if label in issue_labels: + return category + + return "Support" def escape_markdown(text): #tmp = text.replace('&', '&').replace('<', '<').replace('>', '>') @@ -91,6 +124,10 @@ def escape_markdown(text): def format_labels(issue): labels = filter(lambda label: label not in ignored_labels, [label["name"] for label in issue["labels"]]) + # Mark PRs as custom label + if "pull_request" in issue: + labels.append("PR") + if len(labels): return " (" + ", ".join(labels) + ")" else: @@ -175,7 +212,7 @@ for milestone in sorted(milestones.values(), key=lambda ms: (ms["due_on"], ms["t if len(ms_description) > 0: write_changelog("### Notes\n\n" + ms_description + "\n") # Don't escape anything, we take care on Github for valid Markdown - for category in ["Enhancement", "Bug", "Support"]: + for category, labels in categories.iteritems(): try: ms_issues = issues[milestone["title"]][category] except KeyError: From 4fda5e4975dd0d0892d696a28894f3af21881308 Mon Sep 17 00:00:00 2001 From: cstegm Date: Thu, 11 Jan 2018 16:48:40 +0100 Subject: [PATCH 29/35] Update 08-advanced-topics.md fixed typo refs #5972 --- doc/08-advanced-topics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/08-advanced-topics.md b/doc/08-advanced-topics.md index 0b130a0ef..b0c2b79a0 100644 --- a/doc/08-advanced-topics.md +++ b/doc/08-advanced-topics.md @@ -403,7 +403,7 @@ apply Service "external-check" { } ``` -References: [get_service](18-library-reference.md#objref-get_service), [nacro](18-library-reference.md#scoped-functions-macro), [DateTime](18-library-reference.md#datetime-type). +References: [get_service](18-library-reference.md#objref-get_service), [macro](18-library-reference.md#scoped-functions-macro), [DateTime](18-library-reference.md#datetime-type). Example output in Icinga Web 2: From a22e130961073320addaf316fe277bd2a71e8e57 Mon Sep 17 00:00:00 2001 From: Michael Insel Date: Thu, 28 Dec 2017 12:03:49 +0100 Subject: [PATCH 30/35] Fix typo in SELinux documentation refs #5918 --- doc/22-selinux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/22-selinux.md b/doc/22-selinux.md index e1ceedb39..20a0cfff4 100644 --- a/doc/22-selinux.md +++ b/doc/22-selinux.md @@ -104,7 +104,7 @@ SELinux is based on the least level of access required for a service to run. Usi **icinga2_can_connect_all** -Having this boolean enabled allows icinga2 to connect to all ports. This can be neccesary if you use features which connect to unconfined services. +Having this boolean enabled allows icinga2 to connect to all ports. This can be necessary if you use features which connect to unconfined services. **httpd_can_write_icinga2_command** From 415621cc56b515444675cd0b866d3af15a12998a Mon Sep 17 00:00:00 2001 From: Michael Insel Date: Fri, 29 Dec 2017 17:54:14 +0100 Subject: [PATCH 31/35] Fix link format in documentation refs #5922 --- doc/10-icinga-template-library.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md index 60a6a3960..4b74930c0 100644 --- a/doc/10-icinga-template-library.md +++ b/doc/10-icinga-template-library.md @@ -2820,7 +2820,7 @@ or the guest system. The [check_mem.pl](https://github.com/justintime/nagios-plugins) plugin checks the memory usage on linux and unix hosts. It is able to count cache memory as free when -compared to thresholds. More details can be found on [this blog entry]((http://sysadminsjourney.com/content/2009/06/04/new-and-improved-checkmempl-nagios-plugin). +compared to thresholds. More details can be found on [this blog entry](http://sysadminsjourney.com/content/2009/06/04/new-and-improved-checkmempl-nagios-plugin). Custom attributes passed as [command parameters](03-monitoring-basics.md#command-passing-parameters): From 18d40193685b8c3c5664658498de367b8896fa3a Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 20 Dec 2017 08:44:06 +0100 Subject: [PATCH 32/35] Enable installing the init scripts on Solaris refs #5892 --- etc/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/CMakeLists.txt b/etc/CMakeLists.txt index 94d1f2133..f6b005d25 100644 --- a/etc/CMakeLists.txt +++ b/etc/CMakeLists.txt @@ -79,7 +79,7 @@ else() install_if_not_exists(icinga2/features-enabled/mainlog.conf ${CMAKE_INSTALL_SYSCONFDIR}/icinga2/features-enabled) endif() -if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +if(${CMAKE_SYSTEM_NAME} MATCHES "(Linux|Solaris|SunOS)") add_subdirectory(initsystem) endif() From 372be299b4e026d6ff57b56b9d2178bb18df49ef Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Wed, 3 Jan 2018 15:22:02 +0100 Subject: [PATCH 33/35] Add some technical insights into the cluster-zone health check and log lag As per @widhalmt request. refs #5942 Signed-off-by: Gunnar Beutner --- doc/19-technical-concepts.md | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/doc/19-technical-concepts.md b/doc/19-technical-concepts.md index 10d0b9a07..1932a6097 100644 --- a/doc/19-technical-concepts.md +++ b/doc/19-technical-concepts.md @@ -262,6 +262,64 @@ That way only one active DB IDO feature writes to the database, even if they are not currently connected in a cluster zone. This prevents data duplication in historical tables. +### Health Checks + +#### cluster-zone + +This built-in check provides the possibility to check for connectivity between +zones. + +If you for example need to know whether the `master` zone is connected and processing +messages with the child zone called `satellite` in this example, you can configure +the [cluster-zone](10-icinga-template-library.md#itl-icinga-cluster-zone) check as new service on all `master` zone hosts. + +``` +vim /etc/zones.d/master/host1.conf + +object Service "cluster-zone-satellite" { + check_command = "cluster-zone" + host_name = "host1" + + vars.cluster_zone = "satellite" +} +``` + +The check itself changes to NOT-OK if one or more child endpoints in the child zone +are not connected to parent zone endpoints. + +In addition to the overall connectivity check, the log lag is calculated based +on the to-be-sent replay log. Each instance stores that for its configured endpoint +objects. + +This health check iterates over the target zone (`cluster_zone`) and their endpoints. + +The log lag is greater than zero if + +* the replay log synchronization is in progress and not yet finished or +* the endpoint is not connected, and no replay log sync happened (obviously). + +The final log lag value is the worst value detected. If satellite1 has a log lag of +`1.5` and satellite2 only has `0.5`, the computed value will be `1.5.`. + +You can control the check state by using optional warning and critical thresholds +for the log lag value. + +If this service exists multiple times, e.g. for each master host object, the log lag +may differ based on the execution time. This happens for example on restart of +an instance when the log replay is in progress and a health check is executed at different +times. +If the endpoint is not connected, both master instances may have saved a different log replay +position from the last synchronisation. + +The lag value is returned as performance metric key `slave_lag`. + +Icinga 2 v2.9+ adds more performance metrics for these values: + +* `last_messages_sent` and `last_messages_received` as UNIX timestamp +* `sum_messages_sent_per_second` and `sum_messages_received_per_second` +* `sum_bytes_sent_per_second` and `sum_bytes_received_per_second` + +