From 9f3a6b92a206effc5ec5922d315c608e800bf04a Mon Sep 17 00:00:00 2001 From: Vytenis Darulis Date: Tue, 27 Oct 2015 17:00:55 +0000 Subject: [PATCH 01/42] Try to queue all PROCESS_FILE commands instead of exploding the stack fixes #10426 Signed-off-by: Michael Friedrich --- lib/icinga/externalcommandprocessor.cpp | 86 +++++++++++++++++++------ lib/icinga/externalcommandprocessor.hpp | 2 + 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index 09eeac2a9..b9f0479c2 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -290,6 +290,44 @@ void ExternalCommandProcessor::StaticInitialize(void) RegisterCommand("DISABLE_SERVICEGROUP_SVC_NOTIFICATIONS", &ExternalCommandProcessor::DisableServicegroupSvcNotifications, 1); } +void ExternalCommandProcessor::ExecuteFromFile(const String& line, std::deque< std::vector >& file_queue) +{ + if (line.IsEmpty()) + return; + + if (line[0] != '[') + BOOST_THROW_EXCEPTION(std::invalid_argument("Missing timestamp in command: " + line)); + + size_t pos = line.FindFirstOf("]"); + + if (pos == String::NPos) + BOOST_THROW_EXCEPTION(std::invalid_argument("Missing timestamp in command: " + line)); + + String timestamp = line.SubStr(1, pos - 1); + String args = line.SubStr(pos + 2, String::NPos); + + double ts = Convert::ToDouble(timestamp); + + if (ts == 0) + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid timestamp in command: " + line)); + + std::vector argv; + boost::algorithm::split(argv, args, boost::is_any_of(";")); + + if (argv.empty()) + BOOST_THROW_EXCEPTION(std::invalid_argument("Missing arguments in command: " + line)); + + std::vector argvExtra(argv.begin() + 1, argv.end()); + + if (argv[0] == "PROCESS_FILE") { + Log(LogDebug, "ExternalCommandProcessor") + << "Enqueing external command file " << argvExtra[0]; + file_queue.push_back(argvExtra); + } else { + Execute(ts, argv[0], argvExtra); + } +} + void ExternalCommandProcessor::ProcessHostCheckResult(double time, const std::vector& arguments) { Host::Ptr host = Host::GetByName(arguments[0]); @@ -897,33 +935,41 @@ void ExternalCommandProcessor::DisableHostgroupPassiveSvcChecks(double, const st void ExternalCommandProcessor::ProcessFile(double, const std::vector& arguments) { - String file = arguments[0]; - int del = Convert::ToLong(arguments[1]); + std::deque< std::vector > file_queue; + file_queue.push_back(arguments); - std::ifstream ifp; - ifp.exceptions(std::ifstream::badbit); + while (!file_queue.empty()) { + std::vector argument = file_queue.front(); + file_queue.pop_front(); - ifp.open(file.CStr(), std::ifstream::in); + String file = argument[0]; + int to_delete = Convert::ToLong(argument[1]); - while (ifp.good()) { - std::string line; - std::getline(ifp, line); + std::ifstream ifp; + ifp.exceptions(std::ifstream::badbit); - try { - Log(LogNotice, "compat") - << "Executing external command: " << line; + ifp.open(file.CStr(), std::ifstream::in); - Execute(line); - } catch (const std::exception& ex) { - Log(LogWarning, "ExternalCommandProcessor") - << "External command failed: " << DiagnosticInformation(ex); + while (ifp.good()) { + std::string line; + std::getline(ifp, line); + + try { + Log(LogNotice, "compat") + << "Executing external command: " << line; + + ExecuteFromFile(line, file_queue); + } catch (const std::exception& ex) { + Log(LogWarning, "ExternalCommandProcessor") + << "External command failed: " << DiagnosticInformation(ex); + } } + + ifp.close(); + + if (to_delete > 0) + (void) unlink(file.CStr()); } - - ifp.close(); - - if (del > 0) - (void) unlink(file.CStr()); } void ExternalCommandProcessor::ScheduleSvcDowntime(double, const std::vector& arguments) diff --git a/lib/icinga/externalcommandprocessor.hpp b/lib/icinga/externalcommandprocessor.hpp index 836b241ca..059653295 100644 --- a/lib/icinga/externalcommandprocessor.hpp +++ b/lib/icinga/externalcommandprocessor.hpp @@ -42,6 +42,8 @@ public: private: ExternalCommandProcessor(void); + static void ExecuteFromFile(const String& line, std::deque< std::vector >& file_queue); + static void ProcessHostCheckResult(double time, const std::vector& arguments); static void ProcessServiceCheckResult(double time, const std::vector& arguments); static void ScheduleHostCheck(double time, const std::vector& arguments); From 7175174afb6af112fa7a842cb898de4abcbe418f Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Wed, 24 Feb 2016 23:25:59 +0100 Subject: [PATCH 02/42] Update AUTHORS refs #10426 --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 44571e1ba..4019bbcac 100644 --- a/AUTHORS +++ b/AUTHORS @@ -88,6 +88,7 @@ Tobias Birnbaum Tobias von der Krone Tom Geissler Valentin Hoebel +Vytenis Darulis Wolfgang Nieder Yohan Jarosz Zoltan Nagy From 0df7327947cc6c1651af13794f07590428d8e0ce Mon Sep 17 00:00:00 2001 From: Dirk Goetz Date: Mon, 9 Nov 2015 14:26:11 +0100 Subject: [PATCH 03/42] ITL: Add icingacli command Including the subcommand 'businessprocess'. fixes #10581 --- doc/7-icinga-template-library.md | 17 ++++++++++ itl/plugins-contrib.d/icingacli.conf | 49 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 itl/plugins-contrib.d/icingacli.conf diff --git a/doc/7-icinga-template-library.md b/doc/7-icinga-template-library.md index 225add6b9..96cdb2a05 100644 --- a/doc/7-icinga-template-library.md +++ b/doc/7-icinga-template-library.md @@ -1844,6 +1844,23 @@ hpasm_servertype | **Optional.** The type of the server: proliant (default) or hpasm_eval-nics | **Optional.** Check network interfaces (and groups). Try it and report me whyt you think about it. I need to build up some know how on this subject. If get an error and you think, it is not justified for your configuration, please tell me about it. (alwasy send the output of "snmpwalk -On .... 1.3.6.1.4.1.232" and a description how you setup your nics and why it is correct opposed to the plugins error message. +### IcingaCLI + +This category includes all plugins using the icingacli provided by Icinga Web 2. + +#### Business Process + +This subcommand is provided by the [business process module](https://exchange.icinga.org/icinga/Business+Process) and executed as `icingacli-businessprocess`. The module is hosted by the Icinga project on its [project homepage](https://dev.icinga.org/projects/icingaweb2-module-businessprocess). + +Custom attributes passed as [command parameters](3-monitoring-basics.md#command-passing-parameters): + +Name | Description +------------------------------------------|----------------------------------------------------------------------------------------- +icingacli_businessprocess_process | **Required.** Business process to monitor. +icingacli_businessprocess_config | **Optional.** Configuration file containing your business process without file extension. +icingacli_businessprocess_details | **Optional.** Get details for root cause analyses. Defaults to false. + + ### IPMI Devices This category includes all plugins for IPMI devices. diff --git a/itl/plugins-contrib.d/icingacli.conf b/itl/plugins-contrib.d/icingacli.conf new file mode 100644 index 000000000..38d47a0af --- /dev/null +++ b/itl/plugins-contrib.d/icingacli.conf @@ -0,0 +1,49 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2016 Icinga Development Team (https://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. * + ******************************************************************************/ + +template CheckCommand "icingacli" { + import "plugin-check-command" + + command = [ PrefixDir + "/bin/icingacli" ] +} + +object CheckCommand "icingacli-businessprocess" { + import "icingacli" + + command += [ "businessprocess", "check", "process" ] + + arguments = { + "--config" = { + value = "$icingacli_businessprocess_config$" + description = "Configuration file containing your business process without file extension" + } + "--details" = { + set_if = "$icingacli_businessprocess_details$" + description = "Get details for root cause analyses" + } + "--process" = { + value = "$icingacli_businessprocess_process$" + description = "Business process to monitor" + skip_key = true + required = true + } + } + + vars.icingacli_businessprocess_details = false +} From 54dfdb6be34c406daceb6047f119a5fd88537e88 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Sun, 10 Apr 2016 17:47:44 +0200 Subject: [PATCH 04/42] Add missing plugins-contrib install refs #10581 --- itl/plugins-contrib.d/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itl/plugins-contrib.d/CMakeLists.txt b/itl/plugins-contrib.d/CMakeLists.txt index f226e2a29..0a2ec392c 100644 --- a/itl/plugins-contrib.d/CMakeLists.txt +++ b/itl/plugins-contrib.d/CMakeLists.txt @@ -16,6 +16,6 @@ # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. install( - FILES databases.conf ipmi.conf network-components.conf operating-system.conf virtualization.conf vmware.conf web.conf hardware.conf + FILES databases.conf icingacli.conf ipmi.conf network-components.conf operating-system.conf virtualization.conf vmware.conf web.conf hardware.conf DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2/include/plugins-contrib.d ) From 07aaeb2becd2e7177e47b25379da2fe62cdc45ac Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 21 Apr 2016 12:53:55 +0200 Subject: [PATCH 05/42] Fix incorrect chapter headers fixes #11645 --- doc/19-library-reference.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/19-library-reference.md b/doc/19-library-reference.md index 503de7b98..8fffb8288 100644 --- a/doc/19-library-reference.md +++ b/doc/19-library-reference.md @@ -530,7 +530,7 @@ Returns a copy of the object. Note that for object elements which are reference values (e.g. objects such as arrays or dictionaries) the entire object is recursively copied. -## Object#to_string +### Object#to_string Signature: @@ -544,7 +544,7 @@ Example: [ 3, true ].to_string() /* Returns "[ 3.000000, true ]" */ -## Object#type +### Object#type Signature: From 597d300e20c78143b7f0de72526992d040d4de80 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 21 Apr 2016 13:50:47 +0200 Subject: [PATCH 06/42] Implement validation for the Logger#severity attribute fixes #11646 --- lib/base/logger.cpp | 17 ++++++++++++----- lib/base/logger.hpp | 2 ++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/base/logger.cpp b/lib/base/logger.cpp index 3aa14c1a8..d4a204855 100644 --- a/lib/base/logger.cpp +++ b/lib/base/logger.cpp @@ -156,7 +156,6 @@ String Logger::SeverityToString(LogSeverity severity) case LogCritical: return "critical"; default: - Log(LogCritical, "Logger", "Invalid severity."); BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid severity.")); } } @@ -178,11 +177,8 @@ LogSeverity Logger::StringToSeverity(const String& severity) return LogWarning; else if (severity == "critical") return LogCritical; - else { - Log(LogCritical, "Logger") - << "Invalid severity: '" << severity << "'."; + else BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid severity: " + severity)); - } } void Logger::DisableConsoleLog(void) @@ -219,3 +215,14 @@ bool Logger::IsTimestampEnabled(void) { return m_TimestampEnabled; } + +void Logger::ValidateSeverity(const String& value, const ValidationUtils& utils) +{ + ObjectImpl::ValidateSeverity(value, utils); + + try { + StringToSeverity(value); + } catch (...) { + BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("severity"), "Invalid severity specified: " + value)); + } +} diff --git a/lib/base/logger.hpp b/lib/base/logger.hpp index 2a77d0fba..36e727d0e 100644 --- a/lib/base/logger.hpp +++ b/lib/base/logger.hpp @@ -92,6 +92,8 @@ public: static void StaticInitialize(void); + virtual void ValidateSeverity(const String& value, const ValidationUtils& utils) override; + protected: virtual void Start(bool runtimeCreated) override; virtual void Stop(bool runtimeRemoved) override; From f177794d961fc822a826a8cb90cf15db6fa3f08c Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Thu, 21 Apr 2016 14:03:42 +0200 Subject: [PATCH 07/42] Update chocolatey packages and RELEASE.md fixes #11647 --- RELEASE.md | 15 +++++++++++++++ tools/chocolateyInstall.ps1 | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index 0b21f2ed4..b8c2c6ca2 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -130,6 +130,21 @@ Example for CentOS7: Create a new release for the newly created Git tag. https://github.com/Icinga/icinga2/releases +## Chocolatey + +Navigate to the git repository on your Windows box which +already has chocolatey installed. Pull/checkout the release. + +Create the nupkg package: + + cpack + +Install the created icinga2 package locally: + + choco install icinga2 -version 2.4.7 -fdv "%cd%" -source "'%cd%;https://chocolatey.org/api/v2/'" + +Upload the package to [chocolatey](https://chocolatey.org/packages/upload). + ## Online Documentation SSH into the web box, navigate into `icinga2-latest/module/icinga2` diff --git a/tools/chocolateyInstall.ps1 b/tools/chocolateyInstall.ps1 index ad5a282ca..b31c84f24 100755 --- a/tools/chocolateyInstall.ps1 +++ b/tools/chocolateyInstall.ps1 @@ -2,7 +2,7 @@ $installerType = 'msi' $url32 = 'http://packages.icinga.org/windows/Icinga2-v2.4.7-x86.msi' $url64 = 'http://packages.icinga.org/windows/Icinga2-v2.4.7-x86_64.msi' -$silentArgs = '/silent' +$silentArgs = '/qn /norestart' $validExitCodes = @(0) Install-ChocolateyPackage "$packageName" "$installerType" "$silentArgs" "$url32" "$url64" -validExitCodes $validExitCodes From 70c8bbcf99acac912dc6740e6c9b9c0558c2c290 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 21 Apr 2016 15:25:57 +0200 Subject: [PATCH 08/42] Implement support for subjectAltName in SSL certificates fixes #11556 --- lib/base/tlsutility.cpp | 42 +++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/base/tlsutility.cpp b/lib/base/tlsutility.cpp index 5040013cc..83bb7c1bd 100644 --- a/lib/base/tlsutility.cpp +++ b/lib/base/tlsutility.cpp @@ -191,19 +191,12 @@ void AddCRLToSSLContext(const boost::shared_ptr& context, const String& X509_VERIFY_PARAM_free(param); } -/** - * Retrieves the common name for an X509 certificate. - * - * @param certificate The X509 certificate. - * @returns The common name. - */ -String GetCertificateCN(const boost::shared_ptr& certificate) +static String GetX509NameCN(X509_NAME *name) { char errbuf[120]; char buffer[256]; - int rc = X509_NAME_get_text_by_NID(X509_get_subject_name(certificate.get()), - NID_commonName, buffer, sizeof(buffer)); + int rc = X509_NAME_get_text_by_NID(name, NID_commonName, buffer, sizeof(buffer)); if (rc == -1) { Log(LogCritical, "SSL") @@ -216,6 +209,17 @@ String GetCertificateCN(const boost::shared_ptr& certificate) return buffer; } +/** + * Retrieves the common name for an X509 certificate. + * + * @param certificate The X509 certificate. + * @returns The common name. + */ +String GetCertificateCN(const boost::shared_ptr& certificate) +{ + return GetX509NameCN(X509_get_subject_name(certificate.get())); +} + /** * Retrieves an X509 certificate from the specified file. * @@ -416,7 +420,6 @@ boost::shared_ptr CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NA ASN1_INTEGER_set(X509_get_serialNumber(cert), serial); - X509_EXTENSION *ext; X509V3_CTX ctx; X509V3_set_ctx_nodb(&ctx); X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0); @@ -428,12 +431,23 @@ boost::shared_ptr CreateCert(EVP_PKEY *pubkey, X509_NAME *subject, X509_NA else attr = "critical,CA:FALSE"; - ext = X509V3_EXT_conf_nid(NULL, &ctx, NID_basic_constraints, const_cast(attr)); + X509_EXTENSION *basicConstraintsExt = X509V3_EXT_conf_nid(NULL, &ctx, NID_basic_constraints, const_cast(attr)); - if (ext) - X509_add_ext(cert, ext, -1); + if (basicConstraintsExt) { + X509_add_ext(cert, basicConstraintsExt, -1); + X509_EXTENSION_free(basicConstraintsExt); + } - X509_EXTENSION_free(ext); + String cn = GetX509NameCN(subject); + + if (!cn.Contains(" ") && cn.Contains(".")) { + String san = "DNS:" + cn; + X509_EXTENSION *subjectAltNameExt = X509V3_EXT_conf_nid(NULL, &ctx, NID_subject_alt_name, const_cast(san.CStr())); + if (subjectAltNameExt) { + X509_add_ext(cert, subjectAltNameExt, -1); + X509_EXTENSION_free(subjectAltNameExt); + } + } X509_sign(cert, cakey, EVP_sha256()); From 4558488d97084453c8548d7e9d5112f03708d5da Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 21 Apr 2016 15:44:51 +0200 Subject: [PATCH 09/42] Implement SNI support for the CLI commands fixes #11651 --- lib/cli/pkiutility.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cli/pkiutility.cpp b/lib/cli/pkiutility.cpp index e80455b76..ea893e2aa 100644 --- a/lib/cli/pkiutility.cpp +++ b/lib/cli/pkiutility.cpp @@ -137,7 +137,7 @@ boost::shared_ptr PkiUtility::FetchCert(const String& host, const String& return boost::shared_ptr(); } - TlsStream::Ptr stream = new TlsStream(client, String(), RoleClient, sslContext); + TlsStream::Ptr stream = new TlsStream(client, host, RoleClient, sslContext); try { stream->Handshake(); @@ -201,7 +201,7 @@ int PkiUtility::RequestCertificate(const String& host, const String& port, const return 1; } - TlsStream::Ptr stream = new TlsStream(client, String(), RoleClient, sslContext); + TlsStream::Ptr stream = new TlsStream(client, host, RoleClient, sslContext); try { stream->Handshake(); From 418636f7dab84c988b46b8316de52715780cd1fb Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Fri, 22 Apr 2016 11:00:21 +0200 Subject: [PATCH 10/42] Fix crash with empty ScheduledDowntime 'ranges' attribute fixes #11158 --- lib/icinga/scheduleddowntime.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/icinga/scheduleddowntime.cpp b/lib/icinga/scheduleddowntime.cpp index c67bb10b3..4c03f45c9 100644 --- a/lib/icinga/scheduleddowntime.cpp +++ b/lib/icinga/scheduleddowntime.cpp @@ -131,6 +131,9 @@ std::pair ScheduledDowntime::FindNextSegment(void) Dictionary::Ptr ranges = GetRanges(); + if (!ranges) + return std::make_pair(0, 0); + Array::Ptr segments = new Array(); Dictionary::Ptr bestSegment; From 0e70165bc9403395f57c453a05a40ae3f7bbf4d3 Mon Sep 17 00:00:00 2001 From: Jean Flach Date: Mon, 25 Apr 2016 16:52:09 +0200 Subject: [PATCH 11/42] Fix external commands comments crashing icinga Now an exception will be thrown when AddSvcComment or AddHostComment are run with and empty Author or comment fixes #11112 --- lib/icinga/externalcommandprocessor.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index b9f0479c2..2bb225828 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -1228,6 +1228,9 @@ void ExternalCommandProcessor::AddHostComment(double, const std::vector& if (!host) BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot add host comment for non-existent host '" + arguments[0] + "'")); + if (arguments[2].IsEmpty() || arguments[3].IsEmpty()) + BOOST_THROW_EXCEPTION(std::invalid_argument("Author and comment may not be empty")); + Log(LogNotice, "ExternalCommandProcessor") << "Creating comment for host " << host->GetName(); (void) Comment::AddComment(host, CommentUser, arguments[2], arguments[3], 0); @@ -1249,6 +1252,9 @@ void ExternalCommandProcessor::AddSvcComment(double, const std::vector& if (!service) BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot add service comment for non-existent service '" + arguments[1] + "' on host '" + arguments[0] + "'")); + if (arguments[3].IsEmpty() || arguments[4].IsEmpty()) + BOOST_THROW_EXCEPTION(std::invalid_argument("Author and comment may not be empty")); + Log(LogNotice, "ExternalCommandProcessor") << "Creating comment for service " << service->GetName(); (void) Comment::AddComment(service, CommentUser, arguments[3], arguments[4], 0); From e0d1c2f0201b9bcf72b952c025c54986e7a62dcd Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 2 May 2016 15:32:46 +0200 Subject: [PATCH 12/42] Fix: Expired downtimes are not removed fixes #11711 --- lib/compat/statusdatawriter.cpp | 2 +- lib/db_ido/dbevents.cpp | 4 ++-- lib/icinga/checkable-downtime.cpp | 4 ++-- lib/icinga/comment.cpp | 1 + lib/icinga/downtime.cpp | 5 +++-- lib/icinga/downtime.hpp | 2 +- lib/livestatus/downtimestable.cpp | 2 +- 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/compat/statusdatawriter.cpp b/lib/compat/statusdatawriter.cpp index 134174f70..d6073f57c 100644 --- a/lib/compat/statusdatawriter.cpp +++ b/lib/compat/statusdatawriter.cpp @@ -181,7 +181,7 @@ void StatusDataWriter::DumpDowntimes(std::ostream& fp, const Checkable::Ptr& che "\t" "triggered_by=" << triggeredByLegacy << "\n" "\t" "fixed=" << static_cast(downtime->GetFixed()) << "\n" "\t" "duration=" << static_cast(downtime->GetDuration()) << "\n" - "\t" "is_in_effect=" << (downtime->IsActive() ? 1 : 0) << "\n" + "\t" "is_in_effect=" << (downtime->IsInEffect() ? 1 : 0) << "\n" "\t" "author=" << downtime->GetAuthor() << "\n" "\t" "comment=" << downtime->GetComment() << "\n" "\t" "trigger_time=" << downtime->GetTriggerTime() << "\n" diff --git a/lib/db_ido/dbevents.cpp b/lib/db_ido/dbevents.cpp index 1afbe9c3b..fc870f78a 100644 --- a/lib/db_ido/dbevents.cpp +++ b/lib/db_ido/dbevents.cpp @@ -506,7 +506,7 @@ void DbEvents::AddDowntimeInternal(std::vector& queries, const Downtime fields1->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime())); fields1->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime())); fields1->Set("was_started", ((downtime->GetStartTime() <= Utility::GetTime()) ? 1 : 0)); - fields1->Set("is_in_effect", (downtime->IsActive() ? 1 : 0)); + fields1->Set("is_in_effect", (downtime->IsInEffect() ? 1 : 0)); fields1->Set("trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime())); fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */ @@ -658,7 +658,7 @@ void DbEvents::TriggerDowntime(const Downtime::Ptr& downtime) fields1->Set("was_started", 1); fields1->Set("actual_start_time", DbValue::FromTimestamp(time_bag.first)); fields1->Set("actual_start_time_usec", time_bag.second); - fields1->Set("is_in_effect", (downtime->IsActive() ? 1 : 0)); + fields1->Set("is_in_effect", (downtime->IsInEffect() ? 1 : 0)); fields1->Set("trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime())); fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */ diff --git a/lib/icinga/checkable-downtime.cpp b/lib/icinga/checkable-downtime.cpp index 82c0110be..f1eec03ef 100644 --- a/lib/icinga/checkable-downtime.cpp +++ b/lib/icinga/checkable-downtime.cpp @@ -44,7 +44,7 @@ void Checkable::TriggerDowntimes(void) bool Checkable::IsInDowntime(void) const { BOOST_FOREACH(const Downtime::Ptr& downtime, GetDowntimes()) { - if (downtime->IsActive()) + if (downtime->IsInEffect()) return true; } @@ -56,7 +56,7 @@ int Checkable::GetDowntimeDepth(void) const int downtime_depth = 0; BOOST_FOREACH(const Downtime::Ptr& downtime, GetDowntimes()) { - if (downtime->IsActive()) + if (downtime->IsInEffect()) downtime_depth++; } diff --git a/lib/icinga/comment.cpp b/lib/icinga/comment.cpp index 85c5f1a17..eeb7b2b60 100644 --- a/lib/icinga/comment.cpp +++ b/lib/icinga/comment.cpp @@ -244,6 +244,7 @@ void Comment::CommentsExpireTimerHandler(void) } BOOST_FOREACH(const Comment::Ptr& comment, comments) { + /* Only remove comment which are activated after daemon start. */ if (comment->IsActive() && comment->IsExpired()) RemoveComment(comment->GetName()); } diff --git a/lib/icinga/downtime.cpp b/lib/icinga/downtime.cpp index 2d1838302..62bdb7c71 100644 --- a/lib/icinga/downtime.cpp +++ b/lib/icinga/downtime.cpp @@ -150,7 +150,7 @@ Checkable::Ptr Downtime::GetCheckable(void) const return static_pointer_cast(m_Checkable); } -bool Downtime::IsActive(void) const +bool Downtime::IsInEffect(void) const { double now = Utility::GetTime(); @@ -294,7 +294,7 @@ void Downtime::RemoveDowntime(const String& id, bool cancelled, bool expired, co void Downtime::TriggerDowntime(void) { - if (IsActive() && IsTriggered()) { + if (IsInEffect() && IsTriggered()) { Log(LogDebug, "Downtime") << "Not triggering downtime '" << GetName() << "': already triggered."; return; @@ -358,6 +358,7 @@ void Downtime::DowntimesExpireTimerHandler(void) } BOOST_FOREACH(const Downtime::Ptr& downtime, downtimes) { + /* Only remove downtimes which are activated after daemon start. */ if (downtime->IsActive() && downtime->IsExpired()) RemoveDowntime(downtime->GetName(), false, true); } diff --git a/lib/icinga/downtime.hpp b/lib/icinga/downtime.hpp index c2185cc75..e2ef520f6 100644 --- a/lib/icinga/downtime.hpp +++ b/lib/icinga/downtime.hpp @@ -45,7 +45,7 @@ public: intrusive_ptr GetCheckable(void) const; - bool IsActive(void) const; + bool IsInEffect(void) const; bool IsTriggered(void) const; bool IsExpired(void) const; diff --git a/lib/livestatus/downtimestable.cpp b/lib/livestatus/downtimestable.cpp index a5798bfb9..db02a9e4e 100644 --- a/lib/livestatus/downtimestable.cpp +++ b/lib/livestatus/downtimestable.cpp @@ -129,7 +129,7 @@ Value DowntimesTable::TypeAccessor(const Value& row) { Downtime::Ptr downtime = static_cast(row); // 1 .. active, 0 .. pending - return (downtime->IsActive() ? 1 : 0); + return (downtime->IsInEffect() ? 1 : 0); } Value DowntimesTable::IsServiceAccessor(const Value& row) From 7c7a4c3a65a4850ef4536203f0bd8054671b5cab Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Thu, 31 Mar 2016 13:29:08 +0200 Subject: [PATCH 13/42] Remove commentsand downtimes with reference to deleted checkable objects Silence the warning message and change the log level to notice as well. fixes #10717 --- lib/cli/daemonutility.cpp | 4 ++++ lib/config/configitem.cpp | 37 +++++++++++++++++++++++++++++++++---- lib/config/configitem.hpp | 6 +++++- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/lib/cli/daemonutility.cpp b/lib/cli/daemonutility.cpp index a6fe4555f..84470cfab 100644 --- a/lib/cli/daemonutility.cpp +++ b/lib/cli/daemonutility.cpp @@ -24,6 +24,7 @@ #include "config/configcompiler.hpp" #include "config/configcompilercontext.hpp" #include "config/configitembuilder.hpp" +#include "remote/configobjectutility.hpp" using namespace icinga; @@ -168,6 +169,9 @@ bool DaemonUtility::LoadConfigFiles(const std::vector& configs, WorkQueue upq(25000, Application::GetConcurrency()); bool result = ConfigItem::CommitItems(ascope.GetContext(), upq, newItems); + /* Remove ignored Downtime/Comment objects. */ + ConfigItem::RemoveIgnoredItems(ConfigObjectUtility::GetConfigDir()); + if (!result) return false; diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index 267328a8c..f767c4c8d 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -44,6 +44,7 @@ using namespace icinga; boost::mutex ConfigItem::m_Mutex; ConfigItem::TypeMap ConfigItem::m_Items; ConfigItem::ItemList ConfigItem::m_UnnamedItems; +ConfigItem::IgnoredItemList ConfigItem::m_IgnoredItems; REGISTER_SCRIPTFUNCTION(__run_with_activation_context, &ConfigItem::RunWithActivationContext); @@ -190,9 +191,11 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) m_Expression->Evaluate(frame, &debugHints); } catch (const std::exception& ex) { if (m_IgnoreOnError) { - Log(LogWarning, "ConfigObject") + Log(LogNotice, "ConfigObject") << "Ignoring config object '" << m_Name << "' of type '" << m_Type << "' due to errors: " << DiagnosticInformation(ex); + m_IgnoredItems.push_back(m_DebugInfo.Path); + return ConfigObject::Ptr(); } @@ -234,9 +237,11 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) dobj->Validate(FAConfig, utils); } catch (ValidationError& ex) { if (m_IgnoreOnError) { - Log(LogWarning, "ConfigObject") + Log(LogNotice, "ConfigObject") << "Ignoring config object '" << m_Name << "' of type '" << m_Type << "' due to errors: " << DiagnosticInformation(ex); + m_IgnoredItems.push_back(m_DebugInfo.Path); + return ConfigObject::Ptr(); } @@ -248,9 +253,11 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) dobj->OnConfigLoaded(); } catch (const std::exception& ex) { if (m_IgnoreOnError) { - Log(LogWarning, "ConfigObject") + Log(LogNotice, "ConfigObject") << "Ignoring config object '" << m_Name << "' of type '" << m_Type << "' due to errors: " << DiagnosticInformation(ex); + m_IgnoredItems.push_back(m_DebugInfo.Path); + return ConfigObject::Ptr(); } @@ -359,11 +366,13 @@ void ConfigItem::OnAllConfigLoadedHelper(void) m_Object->OnAllConfigLoaded(); } catch (const std::exception& ex) { if (m_IgnoreOnError) { - Log(LogWarning, "ConfigObject") + Log(LogNotice, "ConfigObject") << "Ignoring config object '" << m_Name << "' of type '" << m_Type << "' due to errors: " << DiagnosticInformation(ex); Unregister(); + m_IgnoredItems.push_back(m_DebugInfo.Path); + return; } @@ -630,3 +639,23 @@ std::vector ConfigItem::GetItems(const String& type) return items; } + +void ConfigItem::RemoveIgnoredItems(const String& allowedConfigPath) +{ + BOOST_FOREACH(const String& path, m_IgnoredItems) { + if (path.Find(allowedConfigPath) == String::NPos) + continue; + + Log(LogNotice, "ConfigItem") + << "Removing ignored item path '" << path << "'."; + + if (unlink(path.CStr()) < 0) { + BOOST_THROW_EXCEPTION(posix_error() + << boost::errinfo_api_function("unlink") + << boost::errinfo_errno(errno) + << boost::errinfo_file_name(path)); + } + } + + m_IgnoredItems.clear(); +} diff --git a/lib/config/configitem.hpp b/lib/config/configitem.hpp index 3bfb95334..b9c23d075 100644 --- a/lib/config/configitem.hpp +++ b/lib/config/configitem.hpp @@ -75,6 +75,8 @@ public: static std::vector GetItems(const String& type); + static void RemoveIgnoredItems(const String& allowedConfigPath); + private: String m_Type; /**< The object type. */ String m_Name; /**< The name. */ @@ -99,7 +101,9 @@ private: typedef std::vector ItemList; static ItemList m_UnnamedItems; - static ItemList m_CommittedItems; + + typedef std::vector IgnoredItemList; + static IgnoredItemList m_IgnoredItems; static ConfigItem::Ptr GetObjectUnlocked(const String& type, const String& name); From 65e91ed96bcae4325a656ccfde62ff809654e284 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Fri, 6 May 2016 16:05:40 +0200 Subject: [PATCH 14/42] 'disk' CheckCommand: Exclude 'cgroup' and 'tracefs' by default fixes #11720 --- itl/command-plugins.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itl/command-plugins.conf b/itl/command-plugins.conf index 017f2f213..391184cfb 100644 --- a/itl/command-plugins.conf +++ b/itl/command-plugins.conf @@ -888,7 +888,7 @@ object CheckCommand "disk" { vars.disk_inode_wfree = "20%" vars.disk_inode_cfree = "10%" vars.disk_megabytes = true - vars.disk_exclude_type = ["none", "tmpfs", "sysfs", "proc", "devtmpfs", "devfs", "mtmfs"] + vars.disk_exclude_type = [ "none", "tmpfs", "sysfs", "proc", "devtmpfs", "devfs", "mtmfs", "tracefs", "cgroup" ] } object CheckCommand "disk_smb" { From d82db2ae6cf03f461748218f78f81dafc3307a47 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Mon, 9 May 2016 13:48:30 +0200 Subject: [PATCH 15/42] Make sure the dependency graph is properly updated when adding and removing objects fixes #11686 fixes #11374 --- lib/base/dependencygraph.cpp | 15 ++++++++++++++- lib/icinga/comment.ti | 2 +- lib/icinga/dependency.ti | 4 ++-- lib/icinga/downtime.ti | 2 +- lib/icinga/notification.ti | 2 +- lib/icinga/scheduleddowntime.ti | 2 +- tools/mkclass/classcompiler.cpp | 4 ++-- 7 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/base/dependencygraph.cpp b/lib/base/dependencygraph.cpp index 6f1053c88..037830d56 100644 --- a/lib/base/dependencygraph.cpp +++ b/lib/base/dependencygraph.cpp @@ -34,7 +34,20 @@ void DependencyGraph::AddDependency(Object *parent, Object *child) void DependencyGraph::RemoveDependency(Object *parent, Object *child) { boost::mutex::scoped_lock lock(m_Mutex); - m_Dependencies[child][parent]--; + + std::map& refs = m_Dependencies[child]; + std::map::iterator it = refs.find(parent); + + if (it == refs.end()) + return; + + it->second--; + + if (it->second == 0) + refs.erase(it); + + if (refs.empty()) + m_Dependencies.erase(child); } std::vector DependencyGraph::GetParents(const Object::Ptr& child) diff --git a/lib/icinga/comment.ti b/lib/icinga/comment.ti index 8ac497aac..94ad48ebb 100644 --- a/lib/icinga/comment.ti +++ b/lib/icinga/comment.ti @@ -67,7 +67,7 @@ class Comment : ConfigObject < CommentNameComposer if (!newValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), newValue); - DependencyGraph::RemoveDependency(this, service.get()); + DependencyGraph::AddDependency(this, service.get()); } }}} navigate {{{ diff --git a/lib/icinga/dependency.ti b/lib/icinga/dependency.ti index aeb943fc7..fbbb8246c 100644 --- a/lib/icinga/dependency.ti +++ b/lib/icinga/dependency.ti @@ -55,7 +55,7 @@ class Dependency : CustomVarObject < DependencyNameComposer if (!newValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetParentHostName(), newValue); - DependencyGraph::RemoveDependency(this, service.get()); + DependencyGraph::AddDependency(this, service.get()); } }}} navigate {{{ @@ -82,7 +82,7 @@ class Dependency : CustomVarObject < DependencyNameComposer if (!newValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetParentHostName(), newValue); - DependencyGraph::RemoveDependency(this, service.get()); + DependencyGraph::AddDependency(this, service.get()); } }}} navigate {{{ diff --git a/lib/icinga/downtime.ti b/lib/icinga/downtime.ti index d5a16988a..5c7a19bdd 100644 --- a/lib/icinga/downtime.ti +++ b/lib/icinga/downtime.ti @@ -54,7 +54,7 @@ class Downtime : ConfigObject < DowntimeNameComposer if (!newValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), newValue); - DependencyGraph::RemoveDependency(this, service.get()); + DependencyGraph::AddDependency(this, service.get()); } }}} navigate {{{ diff --git a/lib/icinga/notification.ti b/lib/icinga/notification.ti index c8825b31a..18604e2e9 100644 --- a/lib/icinga/notification.ti +++ b/lib/icinga/notification.ti @@ -74,7 +74,7 @@ class Notification : CustomVarObject < NotificationNameComposer if (!newValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), newValue); - DependencyGraph::RemoveDependency(this, service.get()); + DependencyGraph::AddDependency(this, service.get()); } }}} navigate {{{ diff --git a/lib/icinga/scheduleddowntime.ti b/lib/icinga/scheduleddowntime.ti index 4b95ae60c..92b5e4659 100644 --- a/lib/icinga/scheduleddowntime.ti +++ b/lib/icinga/scheduleddowntime.ti @@ -53,7 +53,7 @@ class ScheduledDowntime : CustomVarObject < ScheduledDowntimeNameComposer if (!newValue.IsEmpty()) { Service::Ptr service = Service::GetByNamePair(GetHostName(), newValue); - DependencyGraph::RemoveDependency(this, service.get()); + DependencyGraph::AddDependency(this, service.get()); } }}} navigate {{{ diff --git a/tools/mkclass/classcompiler.cpp b/tools/mkclass/classcompiler.cpp index 60ae196c7..b72cd76d4 100644 --- a/tools/mkclass/classcompiler.cpp +++ b/tools/mkclass/classcompiler.cpp @@ -901,7 +901,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) << "\t" << klass.Parent << "::Start(runtimeCreated);" << std::endl << std::endl; for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { - if (!(it->Type.IsName)) + if (!it->Type.IsName && it->TrackAccessor.empty()) continue; m_Impl << "\t" << "Track" << it->GetFriendlyName() << "(Empty, Get" << it->GetFriendlyName() << "());" << std::endl; @@ -913,7 +913,7 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&) << "\t" << klass.Parent << "::Stop(runtimeRemoved);" << std::endl << std::endl; for (it = klass.Fields.begin(); it != klass.Fields.end(); it++) { - if (!(it->Type.IsName)) + if (!it->Type.IsName && it->TrackAccessor.empty()) continue; m_Impl << "\t" << "Track" << it->GetFriendlyName() << "(Get" << it->GetFriendlyName() << "(), Empty);" << std::endl; From 83985f97af89c99efe2631a60b4ee910f960b216 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 9 May 2016 15:08:35 +0200 Subject: [PATCH 16/42] API: Fix error message with PUT requests for existing objects fixes #11396 --- lib/remote/configobjectutility.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/remote/configobjectutility.cpp b/lib/remote/configobjectutility.cpp index 13ff4841d..0656aac79 100644 --- a/lib/remote/configobjectutility.cpp +++ b/lib/remote/configobjectutility.cpp @@ -111,8 +111,10 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full String path = GetObjectConfigPath(type, fullName); Utility::MkDirP(Utility::DirName(path), 0700); - if (Utility::PathExists(path)) - BOOST_THROW_EXCEPTION(ScriptError("Configuration file '" + path + "' already exists.")); + if (Utility::PathExists(path)) { + errors->Add("Configuration file '" + path + "' already exists."); + return false; + } std::ofstream fp(path.CStr(), std::ofstream::out | std::ostream::trunc); fp << config; From 0bf075baea2d9b7667a8f2ff460d855036bc32ff Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 9 May 2016 17:42:56 +0200 Subject: [PATCH 17/42] Docs: Add a note for event command execution w/ command execution bridge clients fixes #11742 --- doc/3-monitoring-basics.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/3-monitoring-basics.md b/doc/3-monitoring-basics.md index f2510a1c2..7c81fcd32 100644 --- a/doc/3-monitoring-basics.md +++ b/doc/3-monitoring-basics.md @@ -1612,6 +1612,10 @@ available through runtime vars. Runtime macros such as `$service.state_type$` and `$service.state$` will be processed by Icinga 2 helping on fine-granular events being triggered. +If you are using a client as [command execution bridge](11-icinga2-client.md#icinga2-client-configuration-command-bridge) +the event command will be executed on the client itself (similar to the check +command). + Common use case scenarios are a failing HTTP check requiring an immediate restart via event command, or if an application is locked and requires a restart upon detection. @@ -1621,7 +1625,7 @@ to support native plugin based checks. #### Use Event Commands to Restart Service Daemon -The following example will triggert a restart of the `httpd` daemon +The following example will trigger a restart of the `httpd` daemon via ssh when the `http` service check fails. If the service state is `OK`, it will not trigger any event action. From f2e66941790dbb2b26e0b24d388cf5268e62c517 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 10 May 2016 07:05:10 +0200 Subject: [PATCH 18/42] Remove unused cluster commands fixes #11748 --- lib/icinga/externalcommandprocessor.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index 2bb225828..6d1711d08 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -67,22 +67,6 @@ static std::map& GetCommands(void) boost::signals2::signal&)> ExternalCommandProcessor::OnNewExternalCommand; -static Value ExternalCommandAPIWrapper(const String& command, const Dictionary::Ptr& params) -{ - std::vector arguments; - - if (params) { - int i = 0; - while (params->Contains("arg" + Convert::ToString(i))) { - arguments.push_back(params->Get("arg" + Convert::ToString(i))); - i++; - } - } - - ExternalCommandProcessor::Execute(Utility::GetTime(), command, arguments); - return true; -} - static void RegisterCommand(const String& command, const ExternalCommandCallback& callback, size_t minArgs = 0, size_t maxArgs = UINT_MAX) { boost::mutex::scoped_lock lock(GetMutex()); @@ -91,9 +75,6 @@ static void RegisterCommand(const String& command, const ExternalCommandCallback eci.MinArgs = minArgs; eci.MaxArgs = (maxArgs == UINT_MAX) ? minArgs : maxArgs; GetCommands()[command] = eci; - - ApiFunction::Ptr afunc = new ApiFunction(boost::bind(&ExternalCommandAPIWrapper, command, _2)); - ApiFunction::Register("extcmd::" + command, afunc); } void ExternalCommandProcessor::Execute(const String& line) From 7f8a921f53736490a36bc1de87a6c40f29fdaf53 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 10 May 2016 09:44:41 +0200 Subject: [PATCH 19/42] Fix crash in UnameHelper() fixes #11714 --- lib/base/utility.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp index 0b09516b7..10491d9c4 100644 --- a/lib/base/utility.cpp +++ b/lib/base/utility.cpp @@ -1435,6 +1435,9 @@ static String UnameHelper(char type) FILE *fp = popen(cmd, "r"); + if (!fp) + return "Unknown"; + char line[1024]; std::ostringstream msgbuf; @@ -1892,4 +1895,4 @@ String Utility::GetIcingaDataPath(void) return String(path) + "\\icinga2"; } -#endif /* _WIN32 */ \ No newline at end of file +#endif /* _WIN32 */ From f08d378202683fd6f61e6bfba08e5e59ff26a4f1 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 10 May 2016 11:12:37 +0200 Subject: [PATCH 20/42] Implement support for limiting the number of concurrent checks fixes #8137 --- doc/6-object-types.md | 8 +++++- lib/checker/checkercomponent.cpp | 48 +++++++++++++++++--------------- lib/checker/checkercomponent.hpp | 1 + lib/checker/checkercomponent.ti | 5 ++++ lib/icinga/checkable-check.cpp | 8 +++--- 5 files changed, 43 insertions(+), 27 deletions(-) diff --git a/doc/6-object-types.md b/doc/6-object-types.md index c303fbea9..4fa62809d 100644 --- a/doc/6-object-types.md +++ b/doc/6-object-types.md @@ -189,7 +189,7 @@ Argument array `repeat_key = false`: ## CheckerComponent -The checker component is responsible for scheduling active checks. There are no configurable options. +The checker component is responsible for scheduling active checks. Example: @@ -197,6 +197,12 @@ Example: object CheckerComponent "checker" { } +Configuration Attributes: + + Name |Description + --------------------|---------------- + concurrent\_checks |**Optional.** The maximum number of concurrent checks. Defaults to 512. + ## CheckResultReader Reads Icinga 1.x check results from a directory. This functionality is provided diff --git a/lib/checker/checkercomponent.cpp b/lib/checker/checkercomponent.cpp index 64c37c7e9..6b20cc471 100644 --- a/lib/checker/checkercomponent.cpp +++ b/lib/checker/checkercomponent.cpp @@ -69,6 +69,7 @@ void CheckerComponent::OnConfigLoaded(void) ConfigObject::OnActiveChanged.connect(bind(&CheckerComponent::ObjectHandler, this, _1)); ConfigObject::OnPausedChanged.connect(bind(&CheckerComponent::ObjectHandler, this, _1)); + Checkable::OnNewCheckResult.connect(bind(&CheckerComponent::CheckResultHandler, this, _1)); Checkable::OnNextCheckChanged.connect(bind(&CheckerComponent::NextCheckChangedHandler, this, _1)); } @@ -121,7 +122,7 @@ void CheckerComponent::CheckThreadProc(void) double wait = checkable->GetNextCheck() - Utility::GetTime(); - if (wait > 0) { + if (wait > 0 || m_PendingCheckables.size() >= GetConcurrentChecks()) { /* Wait for the next check. */ m_CV.timed_wait(lock, boost::posix_time::milliseconds(wait * 1000)); @@ -215,27 +216,6 @@ void CheckerComponent::ExecuteCheckHelper(const Checkable::Ptr& checkable) Log(LogCritical, "checker", output); } - - { - boost::mutex::scoped_lock lock(m_Mutex); - - /* remove the object from the list of pending objects; if it's not in the - * list this was a manual (i.e. forced) check and we must not re-add the - * object to the list because it's already there. */ - CheckerComponent::CheckableSet::iterator it; - it = m_PendingCheckables.find(checkable); - if (it != m_PendingCheckables.end()) { - m_PendingCheckables.erase(it); - - if (checkable->IsActive()) - m_IdleCheckables.insert(checkable); - - m_CV.notify_all(); - } - } - - Log(LogDebug, "CheckerComponent") - << "Check finished for object '" << checkable->GetName() << "'"; } void CheckerComponent::ResultTimerHandler(void) @@ -279,6 +259,30 @@ void CheckerComponent::ObjectHandler(const ConfigObject::Ptr& object) } } +void CheckerComponent::CheckResultHandler(const Checkable::Ptr& checkable) +{ + { + boost::mutex::scoped_lock lock(m_Mutex); + + /* remove the object from the list of pending objects; if it's not in the + * list this was a manual (i.e. forced) check and we must not re-add the + * object to the list because it's already there. */ + CheckerComponent::CheckableSet::iterator it; + it = m_PendingCheckables.find(checkable); + if (it != m_PendingCheckables.end()) { + m_PendingCheckables.erase(it); + + if (checkable->IsActive()) + m_IdleCheckables.insert(checkable); + + m_CV.notify_all(); + } + } + + Log(LogDebug, "CheckerComponent") + << "Check finished for object '" << checkable->GetName() << "'"; +} + void CheckerComponent::NextCheckChangedHandler(const Checkable::Ptr& checkable) { boost::mutex::scoped_lock lock(m_Mutex); diff --git a/lib/checker/checkercomponent.hpp b/lib/checker/checkercomponent.hpp index 989668523..f132fe16c 100644 --- a/lib/checker/checkercomponent.hpp +++ b/lib/checker/checkercomponent.hpp @@ -97,6 +97,7 @@ private: void AdjustCheckTimer(void); void ObjectHandler(const ConfigObject::Ptr& object); + void CheckResultHandler(const Checkable::Ptr& checkable); void NextCheckChangedHandler(const Checkable::Ptr& checkable); void RescheduleCheckTimer(void); diff --git a/lib/checker/checkercomponent.ti b/lib/checker/checkercomponent.ti index c35993493..6746b418d 100644 --- a/lib/checker/checkercomponent.ti +++ b/lib/checker/checkercomponent.ti @@ -26,6 +26,11 @@ namespace icinga class CheckerComponent : ConfigObject { + [config] int concurrent_checks { + default {{{ + return 512; + }}} + }; }; } diff --git a/lib/icinga/checkable-check.cpp b/lib/icinga/checkable-check.cpp index 1d5153d57..6e69a8ffa 100644 --- a/lib/icinga/checkable-check.cpp +++ b/lib/icinga/checkable-check.cpp @@ -406,6 +406,10 @@ void Checkable::ExecuteCheck(void) { CONTEXT("Executing check for object '" + GetName() + "'"); + /* keep track of scheduling info in case the check type doesn't provide its own information */ + double scheduled_start = GetNextCheck(); + double before_check = Utility::GetTime(); + UpdateNextCheck(); bool reachable = IsReachable(); @@ -424,10 +428,6 @@ void Checkable::ExecuteCheck(void) SetLastReachable(reachable); } - /* keep track of scheduling info in case the check type doesn't provide its own information */ - double scheduled_start = GetNextCheck(); - double before_check = Utility::GetTime(); - CheckResult::Ptr cr = new CheckResult(); cr->SetScheduleStart(scheduled_start); From b3ad32712e6a5c995d0b5aec8fbe1bf3bc629104 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 10 May 2016 11:44:14 +0200 Subject: [PATCH 21/42] Move CalculateExecutionTime and CalculateLatency into the CheckResult class fixes #11751 --- lib/compat/statusdatawriter.cpp | 4 ++-- lib/db_ido/dbevents.cpp | 4 ++-- lib/db_ido/hostdbobject.cpp | 4 ++-- lib/db_ido/servicedbobject.cpp | 4 ++-- lib/icinga/checkable-check.cpp | 21 --------------------- lib/icinga/checkable.hpp | 3 --- lib/icinga/checkresult.cpp | 15 +++++++++++++++ lib/icinga/checkresult.hpp | 3 +++ lib/icinga/cib.cpp | 14 ++++++++++---- lib/icinga/host.cpp | 4 ++-- lib/icinga/service.cpp | 4 ++-- lib/livestatus/hoststable.cpp | 14 ++++++++++++-- lib/livestatus/servicestable.cpp | 14 ++++++++++++-- lib/perfdata/graphitewriter.cpp | 8 ++++---- lib/perfdata/opentsdbwriter.cpp | 4 ++-- 15 files changed, 70 insertions(+), 50 deletions(-) diff --git a/lib/compat/statusdatawriter.cpp b/lib/compat/statusdatawriter.cpp index d6073f57c..3c88e8a0a 100644 --- a/lib/compat/statusdatawriter.cpp +++ b/lib/compat/statusdatawriter.cpp @@ -335,8 +335,8 @@ void StatusDataWriter::DumpCheckableStatusAttrs(std::ostream& fp, const Checkabl "\t" "event_handler_enabled=" << CompatUtility::GetCheckableEventHandlerEnabled(checkable) << "\n"; if (cr) { - fp << "\t" << "check_execution_time=" << Convert::ToString(Service::CalculateExecutionTime(cr)) << "\n" - "\t" "check_latency=" << Convert::ToString(Service::CalculateLatency(cr)) << "\n"; + fp << "\t" << "check_execution_time=" << Convert::ToString(cr->CalculateExecutionTime()) << "\n" + "\t" "check_latency=" << Convert::ToString(cr->CalculateLatency()) << "\n"; } Host::Ptr host; diff --git a/lib/db_ido/dbevents.cpp b/lib/db_ido/dbevents.cpp index fc870f78a..b9b379632 100644 --- a/lib/db_ido/dbevents.cpp +++ b/lib/db_ido/dbevents.cpp @@ -1391,7 +1391,7 @@ void DbEvents::AddCheckableCheckHistory(const Checkable::Ptr& checkable, const C double end = cr->GetExecutionEnd(); std::pair time_bag_end = CompatUtility::ConvertTimestamp(end); - double execution_time = Service::CalculateExecutionTime(cr); + double execution_time = cr->CalculateExecutionTime(); fields1->Set("start_time", DbValue::FromTimestamp(time_bag_start.first)); fields1->Set("start_time_usec", time_bag_start.second); @@ -1401,7 +1401,7 @@ void DbEvents::AddCheckableCheckHistory(const Checkable::Ptr& checkable, const C fields1->Set("command_args", Empty); fields1->Set("command_line", CompatUtility::GetCommandLine(checkable->GetCheckCommand())); fields1->Set("execution_time", Convert::ToString(execution_time)); - fields1->Set("latency", Convert::ToString(Service::CalculateLatency(cr))); + fields1->Set("latency", Convert::ToString(cr->CalculateLatency())); fields1->Set("return_code", cr->GetExitStatus()); fields1->Set("output", CompatUtility::GetCheckResultOutput(cr)); fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr)); diff --git a/lib/db_ido/hostdbobject.cpp b/lib/db_ido/hostdbobject.cpp index 05ee42a65..47fd7d244 100644 --- a/lib/db_ido/hostdbobject.cpp +++ b/lib/db_ido/hostdbobject.cpp @@ -157,8 +157,8 @@ Dictionary::Ptr HostDbObject::GetStatusFields(void) const fields->Set("percent_state_change", CompatUtility::GetCheckablePercentStateChange(host)); if (cr) { - fields->Set("latency", Convert::ToString(Service::CalculateLatency(cr))); - fields->Set("execution_time", Convert::ToString(Service::CalculateExecutionTime(cr))); + fields->Set("latency", Convert::ToString(cr->CalculateLatency())); + fields->Set("execution_time", Convert::ToString(cr->CalculateExecutionTime())); } fields->Set("scheduled_downtime_depth", host->GetDowntimeDepth()); diff --git a/lib/db_ido/servicedbobject.cpp b/lib/db_ido/servicedbobject.cpp index e56f5fceb..2806e1c70 100644 --- a/lib/db_ido/servicedbobject.cpp +++ b/lib/db_ido/servicedbobject.cpp @@ -153,8 +153,8 @@ Dictionary::Ptr ServiceDbObject::GetStatusFields(void) const fields->Set("percent_state_change", CompatUtility::GetCheckablePercentStateChange(service)); if (cr) { - fields->Set("latency", Convert::ToString(Service::CalculateLatency(cr))); - fields->Set("execution_time", Convert::ToString(Service::CalculateExecutionTime(cr))); + fields->Set("latency", Convert::ToString(cr->CalculateLatency())); + fields->Set("execution_time", Convert::ToString(cr->CalculateExecutionTime())); } fields->Set("scheduled_downtime_depth", service->GetDowntimeDepth()); diff --git a/lib/icinga/checkable-check.cpp b/lib/icinga/checkable-check.cpp index 6e69a8ffa..f23cee7ed 100644 --- a/lib/icinga/checkable-check.cpp +++ b/lib/icinga/checkable-check.cpp @@ -515,24 +515,3 @@ void Checkable::UpdateStatistics(const CheckResult::Ptr& cr, CheckableType type) Log(LogWarning, "Checkable", "Unknown checkable type for statistic update."); } } - -double Checkable::CalculateExecutionTime(const CheckResult::Ptr& cr) -{ - if (!cr) - return 0; - - return cr->GetExecutionEnd() - cr->GetExecutionStart(); -} - -double Checkable::CalculateLatency(const CheckResult::Ptr& cr) -{ - if (!cr) - return 0; - - double latency = (cr->GetScheduleEnd() - cr->GetScheduleStart()) - CalculateExecutionTime(cr); - - if (latency < 0) - latency = 0; - - return latency; -} diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index a576f5768..abd966dfd 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -106,9 +106,6 @@ public: Endpoint::Ptr GetCommandEndpoint(void) const; - static double CalculateExecutionTime(const CheckResult::Ptr& cr); - static double CalculateLatency(const CheckResult::Ptr& cr); - static boost::signals2::signal OnNewCheckResult; static boost::signals2::signal OnStateChange; static boost::signals2::signal, const MessageOrigin::Ptr&)> OnReachabilityChanged; diff --git a/lib/icinga/checkresult.cpp b/lib/icinga/checkresult.cpp index 50028df78..5a17547b5 100644 --- a/lib/icinga/checkresult.cpp +++ b/lib/icinga/checkresult.cpp @@ -36,3 +36,18 @@ void CheckResult::StaticInitialize(void) ScriptGlobal::Set("HostUp", HostUp); ScriptGlobal::Set("HostDown", HostDown); } + +double CheckResult::CalculateExecutionTime(void) const +{ + return GetExecutionEnd() - GetExecutionStart(); +} + +double CheckResult::CalculateLatency(void) const +{ + double latency = (GetScheduleEnd() - GetScheduleStart()) - CalculateExecutionTime(); + + if (latency < 0) + latency = 0; + + return latency; +} diff --git a/lib/icinga/checkresult.hpp b/lib/icinga/checkresult.hpp index 3e4f729bd..b80f6c3d7 100644 --- a/lib/icinga/checkresult.hpp +++ b/lib/icinga/checkresult.hpp @@ -36,6 +36,9 @@ class I2_ICINGA_API CheckResult : public ObjectImpl public: DECLARE_OBJECT(CheckResult); + double CalculateExecutionTime(void) const; + double CalculateLatency(void) const; + static void StaticInitialize(void); }; diff --git a/lib/icinga/cib.cpp b/lib/icinga/cib.cpp index 67c14a120..ccdbc177e 100644 --- a/lib/icinga/cib.cpp +++ b/lib/icinga/cib.cpp @@ -86,8 +86,11 @@ CheckableCheckStatistics CIB::CalculateHostCheckStats(void) CheckResult::Ptr cr = host->GetLastCheckResult(); + if (!cr) + continue; + /* latency */ - double latency = Host::CalculateLatency(cr); + double latency = cr->CalculateLatency(); if (min_latency == -1 || latency < min_latency) min_latency = latency; @@ -99,7 +102,7 @@ CheckableCheckStatistics CIB::CalculateHostCheckStats(void) count_latency++; /* execution_time */ - double execution_time = Host::CalculateExecutionTime(cr); + double execution_time = cr->CalculateExecutionTime(); if (min_execution_time == -1 || execution_time < min_execution_time) min_execution_time = execution_time; @@ -135,8 +138,11 @@ CheckableCheckStatistics CIB::CalculateServiceCheckStats(void) CheckResult::Ptr cr = service->GetLastCheckResult(); + if (!cr) + continue; + /* latency */ - double latency = Service::CalculateLatency(cr); + double latency = cr->CalculateLatency(); if (min_latency == -1 || latency < min_latency) min_latency = latency; @@ -148,7 +154,7 @@ CheckableCheckStatistics CIB::CalculateServiceCheckStats(void) count_latency++; /* execution_time */ - double execution_time = Service::CalculateExecutionTime(cr); + double execution_time = cr->CalculateExecutionTime(); if (min_execution_time == -1 || execution_time < min_execution_time) min_execution_time = execution_time; diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 5fd2dfc47..af33d528b 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -289,10 +289,10 @@ bool Host::ResolveMacro(const String& macro, const CheckResult::Ptr&, Value *res if (cr) { if (macro == "latency") { - *result = Convert::ToString(Service::CalculateLatency(cr)); + *result = Convert::ToString(cr->CalculateLatency()); return true; } else if (macro == "execution_time") { - *result = Convert::ToString(Service::CalculateExecutionTime(cr)); + *result = Convert::ToString(cr->CalculateExecutionTime()); return true; } else if (macro == "output") { *result = cr->GetOutput(); diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index a96d6058d..78b389ce2 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -219,10 +219,10 @@ bool Service::ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Valu if (cr) { if (macro == "latency") { - *result = Convert::ToString(Service::CalculateLatency(cr)); + *result = Convert::ToString(cr->CalculateLatency()); return true; } else if (macro == "execution_time") { - *result = Convert::ToString(Service::CalculateExecutionTime(cr)); + *result = Convert::ToString(cr->CalculateExecutionTime()); return true; } else if (macro == "output") { *result = cr->GetOutput(); diff --git a/lib/livestatus/hoststable.cpp b/lib/livestatus/hoststable.cpp index 168aaf2ad..62da70dd9 100644 --- a/lib/livestatus/hoststable.cpp +++ b/lib/livestatus/hoststable.cpp @@ -836,7 +836,12 @@ Value HostsTable::LatencyAccessor(const Value& row) if (!host) return Empty; - return (Service::CalculateLatency(host->GetLastCheckResult())); + CheckResult::Ptr cr = host->GetLastCheckResult(); + + if (!cr) + return Empty; + + return cr->CalculateLatency(); } Value HostsTable::ExecutionTimeAccessor(const Value& row) @@ -846,7 +851,12 @@ Value HostsTable::ExecutionTimeAccessor(const Value& row) if (!host) return Empty; - return (Service::CalculateExecutionTime(host->GetLastCheckResult())); + CheckResult::Ptr cr = host->GetLastCheckResult(); + + if (!cr) + return Empty; + + return cr->CalculateExecutionTime(); } Value HostsTable::PercentStateChangeAccessor(const Value& row) diff --git a/lib/livestatus/servicestable.cpp b/lib/livestatus/servicestable.cpp index 7b5c04327..95f05212f 100644 --- a/lib/livestatus/servicestable.cpp +++ b/lib/livestatus/servicestable.cpp @@ -874,7 +874,12 @@ Value ServicesTable::LatencyAccessor(const Value& row) if (!service) return Empty; - return (Service::CalculateLatency(service->GetLastCheckResult())); + CheckResult::Ptr cr = service->GetLastCheckResult(); + + if (!cr) + return Empty; + + return cr->CalculateLatency(); } Value ServicesTable::ExecutionTimeAccessor(const Value& row) @@ -884,7 +889,12 @@ Value ServicesTable::ExecutionTimeAccessor(const Value& row) if (!service) return Empty; - return (Service::CalculateExecutionTime(service->GetLastCheckResult())); + CheckResult::Ptr cr = service->GetLastCheckResult(); + + if (!cr) + return Empty; + + return cr->CalculateExecutionTime(); } Value ServicesTable::PercentStateChangeAccessor(const Value& row) diff --git a/lib/perfdata/graphitewriter.cpp b/lib/perfdata/graphitewriter.cpp index fc5987db8..3f59287e5 100644 --- a/lib/perfdata/graphitewriter.cpp +++ b/lib/perfdata/graphitewriter.cpp @@ -141,8 +141,8 @@ void GraphiteWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C SendMetric(prefix_metadata, "state_type", checkable->GetStateType(), ts); SendMetric(prefix_metadata, "reachable", checkable->IsReachable(), ts); SendMetric(prefix_metadata, "downtime_depth", checkable->GetDowntimeDepth(), ts); - SendMetric(prefix_metadata, "latency", Service::CalculateLatency(cr), ts); - SendMetric(prefix_metadata, "execution_time", Service::CalculateExecutionTime(cr), ts); + SendMetric(prefix_metadata, "latency", cr->CalculateLatency(), ts); + SendMetric(prefix_metadata, "execution_time", cr->CalculateExecutionTime(), ts); } SendPerfdata(prefix_perfdata, cr, ts); @@ -160,8 +160,8 @@ void GraphiteWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C SendMetric(prefix, "state_type", checkable->GetStateType(), ts); SendMetric(prefix, "reachable", checkable->IsReachable(), ts); SendMetric(prefix, "downtime_depth", checkable->GetDowntimeDepth(), ts); - SendMetric(prefix, "latency", Service::CalculateLatency(cr), ts); - SendMetric(prefix, "execution_time", Service::CalculateExecutionTime(cr), ts); + SendMetric(prefix, "latency", cr->CalculateLatency(), ts); + SendMetric(prefix, "execution_time", cr->CalculateExecutionTime(), ts); SendPerfdata(prefix, cr, ts); } } diff --git a/lib/perfdata/opentsdbwriter.cpp b/lib/perfdata/opentsdbwriter.cpp index aa8bdcc42..1f9b3896a 100644 --- a/lib/perfdata/opentsdbwriter.cpp +++ b/lib/perfdata/opentsdbwriter.cpp @@ -145,8 +145,8 @@ void OpenTsdbWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C SendMetric(metric + ".current_attempt", tags, checkable->GetCheckAttempt(), ts); SendMetric(metric + ".max_check_attempts", tags, checkable->GetMaxCheckAttempts(), ts); - SendMetric(metric + ".latency", tags, Service::CalculateLatency(cr), ts); - SendMetric(metric + ".execution_time", tags, Service::CalculateExecutionTime(cr), ts); + SendMetric(metric + ".latency", tags, cr->CalculateLatency(), ts); + SendMetric(metric + ".execution_time", tags, cr->CalculateExecutionTime(), ts); } void OpenTsdbWriter::SendPerfdata(const String& metric, const std::map& tags, const CheckResult::Ptr& cr, double ts) From 06f97c87ede7b550069222d8ed74612fc848f38e Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 10 May 2016 13:42:05 +0200 Subject: [PATCH 22/42] API: Fix error message handling for invalid attributes in PUT requests refs #11396 --- lib/remote/createobjecthandler.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index a680feaef..2aca205c4 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -62,14 +62,29 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r if (params->Contains("ignore_on_error")) ignoreOnError = HttpUtility::GetLastParameter(params, "ignore_on_error"); - String config = ConfigObjectUtility::CreateObjectConfig(type, name, ignoreOnError, templates, attrs); - Array::Ptr results = new Array(); results->Add(result1); Dictionary::Ptr result = new Dictionary(); result->Set("results", results); + String config; + + try { + config = ConfigObjectUtility::CreateObjectConfig(type, name, ignoreOnError, templates, attrs); + } catch (const std::exception& ex) { + errors->Add(DiagnosticInformation(ex)); + + result1->Set("errors", errors); + result1->Set("code", 500); + result1->Set("status", "Object could not be created."); + + response.SetStatus(500, "Object could not be created"); + HttpUtility::SendJsonBody(response, result); + + return true; + } + if (!ConfigObjectUtility::CreateObject(type, name, config, errors)) { result1->Set("errors", errors); result1->Set("code", 500); From 7f915dbfafac40ac7f21fb74dd3773e6eef69c0d Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Tue, 10 May 2016 15:16:35 +0200 Subject: [PATCH 23/42] API: Fix error handling for invalid JSON request body fixes #11757 --- lib/remote/actionshandler.cpp | 4 +--- lib/remote/actionshandler.hpp | 3 ++- lib/remote/configfileshandler.cpp | 4 +--- lib/remote/configfileshandler.hpp | 3 ++- lib/remote/configpackageshandler.cpp | 14 +++++--------- lib/remote/configpackageshandler.hpp | 12 ++++++++---- lib/remote/configstageshandler.cpp | 20 +++++++------------- lib/remote/configstageshandler.hpp | 12 ++++++++---- lib/remote/consolehandler.cpp | 5 ++--- lib/remote/consolehandler.hpp | 3 ++- lib/remote/createobjecthandler.cpp | 3 +-- lib/remote/createobjecthandler.hpp | 3 ++- lib/remote/deleteobjecthandler.cpp | 4 +--- lib/remote/deleteobjecthandler.hpp | 3 ++- lib/remote/eventshandler.cpp | 4 +--- lib/remote/eventshandler.hpp | 3 ++- lib/remote/httphandler.cpp | 13 ++++++++++++- lib/remote/httphandler.hpp | 2 +- lib/remote/infohandler.cpp | 2 +- lib/remote/infohandler.hpp | 3 ++- lib/remote/modifyobjecthandler.cpp | 4 +--- lib/remote/modifyobjecthandler.hpp | 3 ++- lib/remote/objectqueryhandler.cpp | 4 +--- lib/remote/objectqueryhandler.hpp | 3 ++- lib/remote/statushandler.cpp | 4 +--- lib/remote/statushandler.hpp | 3 ++- lib/remote/typequeryhandler.cpp | 4 +--- lib/remote/typequeryhandler.hpp | 3 ++- 28 files changed, 75 insertions(+), 73 deletions(-) diff --git a/lib/remote/actionshandler.cpp b/lib/remote/actionshandler.cpp index 1ab952327..998c765d7 100644 --- a/lib/remote/actionshandler.cpp +++ b/lib/remote/actionshandler.cpp @@ -31,7 +31,7 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/actions", ActionsHandler); -bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() != 3) return false; @@ -50,8 +50,6 @@ bool ActionsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& reques QueryDescription qd; - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - const std::vector& types = action->GetTypes(); std::vector objs; diff --git a/lib/remote/actionshandler.hpp b/lib/remote/actionshandler.hpp index 1e7354a33..41d5a5da0 100644 --- a/lib/remote/actionshandler.hpp +++ b/lib/remote/actionshandler.hpp @@ -30,7 +30,8 @@ class I2_REMOTE_API ActionsHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ActionsHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; }; } diff --git a/lib/remote/configfileshandler.cpp b/lib/remote/configfileshandler.cpp index 7de396d69..aee9b7226 100644 --- a/lib/remote/configfileshandler.cpp +++ b/lib/remote/configfileshandler.cpp @@ -29,13 +29,11 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/config/files", ConfigFilesHandler); -bool ConfigFilesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool ConfigFilesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestMethod != "GET") return false; - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - const std::vector& urlPath = request.RequestUrl->GetPath(); if (urlPath.size() >= 4) diff --git a/lib/remote/configfileshandler.hpp b/lib/remote/configfileshandler.hpp index f74cdda8e..d330b981b 100644 --- a/lib/remote/configfileshandler.hpp +++ b/lib/remote/configfileshandler.hpp @@ -30,7 +30,8 @@ class I2_REMOTE_API ConfigFilesHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ConfigFilesHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; }; } diff --git a/lib/remote/configpackageshandler.cpp b/lib/remote/configpackageshandler.cpp index 3ab277b96..dde0350f9 100644 --- a/lib/remote/configpackageshandler.cpp +++ b/lib/remote/configpackageshandler.cpp @@ -28,7 +28,7 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/config/packages", ConfigPackagesHandler); -bool ConfigPackagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool ConfigPackagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() > 4) return false; @@ -36,9 +36,9 @@ bool ConfigPackagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& if (request.RequestMethod == "GET") HandleGet(user, request, response); else if (request.RequestMethod == "POST") - HandlePost(user, request, response); + HandlePost(user, request, response, params); else if (request.RequestMethod == "DELETE") - HandleDelete(user, request, response); + HandleDelete(user, request, response, params); else return false; @@ -68,12 +68,10 @@ void ConfigPackagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& req HttpUtility::SendJsonBody(response, result); } -void ConfigPackagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +void ConfigPackagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { FilterUtility::CheckPermission(user, "config/modify"); - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - if (request.RequestUrl->GetPath().size() >= 4) params->Set("package", request.RequestUrl->GetPath()[3]); @@ -106,12 +104,10 @@ void ConfigPackagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& re HttpUtility::SendJsonBody(response, result); } -void ConfigPackagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +void ConfigPackagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { FilterUtility::CheckPermission(user, "config/modify"); - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - if (request.RequestUrl->GetPath().size() >= 4) params->Set("package", request.RequestUrl->GetPath()[3]); diff --git a/lib/remote/configpackageshandler.hpp b/lib/remote/configpackageshandler.hpp index 41483ce8c..1e0fda6b1 100644 --- a/lib/remote/configpackageshandler.hpp +++ b/lib/remote/configpackageshandler.hpp @@ -30,12 +30,16 @@ class I2_REMOTE_API ConfigPackagesHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ConfigPackagesHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; private: - void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); - void HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); - void HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); + void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response); + void HandlePost(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params); + void HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params); }; diff --git a/lib/remote/configstageshandler.cpp b/lib/remote/configstageshandler.cpp index 5e85d015d..390a176d2 100644 --- a/lib/remote/configstageshandler.cpp +++ b/lib/remote/configstageshandler.cpp @@ -30,29 +30,27 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/config/stages", ConfigStagesHandler); -bool ConfigStagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool ConfigStagesHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() > 5) return false; if (request.RequestMethod == "GET") - HandleGet(user, request, response); + HandleGet(user, request, response, params); else if (request.RequestMethod == "POST") - HandlePost(user, request, response); + HandlePost(user, request, response, params); else if (request.RequestMethod == "DELETE") - HandleDelete(user, request, response); + HandleDelete(user, request, response, params); else return false; return true; } -void ConfigStagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +void ConfigStagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { FilterUtility::CheckPermission(user, "config/query"); - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - if (request.RequestUrl->GetPath().size() >= 4) params->Set("package", request.RequestUrl->GetPath()[3]); @@ -89,12 +87,10 @@ void ConfigStagesHandler::HandleGet(const ApiUser::Ptr& user, HttpRequest& reque HttpUtility::SendJsonBody(response, result); } -void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { FilterUtility::CheckPermission(user, "config/modify"); - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - if (request.RequestUrl->GetPath().size() >= 4) params->Set("package", request.RequestUrl->GetPath()[3]); @@ -138,12 +134,10 @@ void ConfigStagesHandler::HandlePost(const ApiUser::Ptr& user, HttpRequest& requ HttpUtility::SendJsonBody(response, result); } -void ConfigStagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +void ConfigStagesHandler::HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { FilterUtility::CheckPermission(user, "config/modify"); - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - if (request.RequestUrl->GetPath().size() >= 4) params->Set("package", request.RequestUrl->GetPath()[3]); diff --git a/lib/remote/configstageshandler.hpp b/lib/remote/configstageshandler.hpp index 3f33cd8a7..eabb9edd8 100644 --- a/lib/remote/configstageshandler.hpp +++ b/lib/remote/configstageshandler.hpp @@ -30,12 +30,16 @@ class I2_REMOTE_API ConfigStagesHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ConfigStagesHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; private: - void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); - void HandlePost(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); - void HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); + void HandleGet(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params); + void HandlePost(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params); + void HandleDelete(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params); }; diff --git a/lib/remote/consolehandler.cpp b/lib/remote/consolehandler.cpp index a5c3efd0a..7e8c65677 100644 --- a/lib/remote/consolehandler.cpp +++ b/lib/remote/consolehandler.cpp @@ -67,7 +67,7 @@ static void InitScriptFrameCleanup(void) INITIALIZE_ONCE(InitScriptFrameCleanup); -bool ConsoleHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool ConsoleHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() > 3) return false; @@ -76,10 +76,9 @@ bool ConsoleHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& reques return false; QueryDescription qd; - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); String methodName = request.RequestUrl->GetPath()[2]; - + FilterUtility::CheckPermission(user, "console"); String session = HttpUtility::GetLastParameter(params, "session"); diff --git a/lib/remote/consolehandler.hpp b/lib/remote/consolehandler.hpp index 6e8841080..e2627760c 100644 --- a/lib/remote/consolehandler.hpp +++ b/lib/remote/consolehandler.hpp @@ -43,7 +43,8 @@ class I2_REMOTE_API ConsoleHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ConsoleHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; static std::vector GetAutocompletionSuggestions(const String& word, ScriptFrame& frame); diff --git a/lib/remote/createobjecthandler.cpp b/lib/remote/createobjecthandler.cpp index 2aca205c4..2a15976f3 100644 --- a/lib/remote/createobjecthandler.cpp +++ b/lib/remote/createobjecthandler.cpp @@ -30,7 +30,7 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/objects", CreateObjectHandler); -bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() != 4) return false; @@ -48,7 +48,6 @@ bool CreateObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r FilterUtility::CheckPermission(user, "objects/create/" + type->GetName()); String name = request.RequestUrl->GetPath()[3]; - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); Array::Ptr templates = params->Get("templates"); Dictionary::Ptr attrs = params->Get("attrs"); diff --git a/lib/remote/createobjecthandler.hpp b/lib/remote/createobjecthandler.hpp index 52cc51290..df3ec398e 100644 --- a/lib/remote/createobjecthandler.hpp +++ b/lib/remote/createobjecthandler.hpp @@ -30,7 +30,8 @@ class I2_REMOTE_API CreateObjectHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(CreateObjectHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; }; } diff --git a/lib/remote/deleteobjecthandler.cpp b/lib/remote/deleteobjecthandler.cpp index 234e8f60e..4e3356187 100644 --- a/lib/remote/deleteobjecthandler.cpp +++ b/lib/remote/deleteobjecthandler.cpp @@ -32,7 +32,7 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/objects", DeleteObjectHandler); -bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) return false; @@ -51,8 +51,6 @@ bool DeleteObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r qd.Types.insert(type->GetName()); qd.Permission = "objects/delete/" + type->GetName(); - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - params->Set("type", type->GetName()); if (request.RequestUrl->GetPath().size() >= 4) { diff --git a/lib/remote/deleteobjecthandler.hpp b/lib/remote/deleteobjecthandler.hpp index 794455f70..d1a941879 100644 --- a/lib/remote/deleteobjecthandler.hpp +++ b/lib/remote/deleteobjecthandler.hpp @@ -30,7 +30,8 @@ class I2_REMOTE_API DeleteObjectHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(DeleteObjectHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; }; } diff --git a/lib/remote/eventshandler.cpp b/lib/remote/eventshandler.cpp index 6aed8d25d..9129b625d 100644 --- a/lib/remote/eventshandler.cpp +++ b/lib/remote/eventshandler.cpp @@ -31,7 +31,7 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/events", EventsHandler); -bool EventsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool EventsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() != 2) return false; @@ -44,8 +44,6 @@ bool EventsHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request return true; } - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - Array::Ptr types = params->Get("types"); if (!types) { diff --git a/lib/remote/eventshandler.hpp b/lib/remote/eventshandler.hpp index 924f7b36b..9eed187a2 100644 --- a/lib/remote/eventshandler.hpp +++ b/lib/remote/eventshandler.hpp @@ -31,7 +31,8 @@ class I2_REMOTE_API EventsHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(EventsHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; }; } diff --git a/lib/remote/httphandler.cpp b/lib/remote/httphandler.cpp index 6e64ae7d3..24db6ce44 100644 --- a/lib/remote/httphandler.cpp +++ b/lib/remote/httphandler.cpp @@ -20,6 +20,7 @@ #include "remote/httphandler.hpp" #include "remote/httputility.hpp" #include "base/singleton.hpp" +#include "base/exception.hpp" #include using namespace icinga; @@ -94,13 +95,23 @@ void HttpHandler::ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, std::reverse(handlers.begin(), handlers.end()); + Dictionary::Ptr params; + + try { + params = HttpUtility::FetchRequestParameters(request); + } catch (const std::exception& ex) { + HttpUtility::SendJsonError(response, 400, "Invalid request body: " + DiagnosticInformation(ex, false)); + return; + } + bool processed = false; BOOST_FOREACH(const HttpHandler::Ptr& handler, handlers) { - if (handler->HandleRequest(user, request, response)) { + if (handler->HandleRequest(user, request, response, params)) { processed = true; break; } } + if (!processed) { String path = boost::algorithm::join(request.RequestUrl->GetPath(), "/"); HttpUtility::SendJsonError(response, 404, "The requested path '" + path + diff --git a/lib/remote/httphandler.hpp b/lib/remote/httphandler.hpp index fa9187d56..de01969e2 100644 --- a/lib/remote/httphandler.hpp +++ b/lib/remote/httphandler.hpp @@ -40,7 +40,7 @@ class I2_REMOTE_API HttpHandler : public Object public: DECLARE_PTR_TYPEDEFS(HttpHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) = 0; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) = 0; static void Register(const Url::Ptr& url, const HttpHandler::Ptr& handler); static void ProcessRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response); diff --git a/lib/remote/infohandler.cpp b/lib/remote/infohandler.cpp index 11b1536e4..058ca51de 100644 --- a/lib/remote/infohandler.cpp +++ b/lib/remote/infohandler.cpp @@ -24,7 +24,7 @@ using namespace icinga; REGISTER_URLHANDLER("/", InfoHandler); -bool InfoHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool InfoHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() > 2) return false; diff --git a/lib/remote/infohandler.hpp b/lib/remote/infohandler.hpp index 1a68609e4..4b867e4aa 100644 --- a/lib/remote/infohandler.hpp +++ b/lib/remote/infohandler.hpp @@ -30,7 +30,8 @@ class I2_REMOTE_API InfoHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(InfoHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; }; } diff --git a/lib/remote/modifyobjecthandler.cpp b/lib/remote/modifyobjecthandler.cpp index f648a19f2..b97a7d176 100644 --- a/lib/remote/modifyobjecthandler.cpp +++ b/lib/remote/modifyobjecthandler.cpp @@ -30,7 +30,7 @@ using namespace icinga; REGISTER_URLHANDLER("/v1/objects", ModifyObjectHandler); -bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) return false; @@ -49,8 +49,6 @@ bool ModifyObjectHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& r qd.Types.insert(type->GetName()); qd.Permission = "objects/modify/" + type->GetName(); - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - params->Set("type", type->GetName()); if (request.RequestUrl->GetPath().size() >= 4) { diff --git a/lib/remote/modifyobjecthandler.hpp b/lib/remote/modifyobjecthandler.hpp index e71e0eb2b..2f11a0932 100644 --- a/lib/remote/modifyobjecthandler.hpp +++ b/lib/remote/modifyobjecthandler.hpp @@ -30,7 +30,8 @@ class I2_REMOTE_API ModifyObjectHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ModifyObjectHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; }; } diff --git a/lib/remote/objectqueryhandler.cpp b/lib/remote/objectqueryhandler.cpp index 111d284bc..e7bf646c3 100644 --- a/lib/remote/objectqueryhandler.cpp +++ b/lib/remote/objectqueryhandler.cpp @@ -104,7 +104,7 @@ Dictionary::Ptr ObjectQueryHandler::SerializeObjectAttrs(const Object::Ptr& obje return resultAttrs; } -bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4) return false; @@ -123,8 +123,6 @@ bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& re qd.Types.insert(type->GetName()); qd.Permission = "objects/query/" + type->GetName(); - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - Array::Ptr uattrs = params->Get("attrs"); Array::Ptr ujoins = params->Get("joins"); Array::Ptr umetas = params->Get("meta"); diff --git a/lib/remote/objectqueryhandler.hpp b/lib/remote/objectqueryhandler.hpp index 548466b83..5676baea5 100644 --- a/lib/remote/objectqueryhandler.hpp +++ b/lib/remote/objectqueryhandler.hpp @@ -30,7 +30,8 @@ class I2_REMOTE_API ObjectQueryHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(ObjectQueryHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; private: static Dictionary::Ptr SerializeObjectAttrs(const Object::Ptr& object, const String& attrPrefix, diff --git a/lib/remote/statushandler.cpp b/lib/remote/statushandler.cpp index cce71fbef..f8966b5f4 100644 --- a/lib/remote/statushandler.cpp +++ b/lib/remote/statushandler.cpp @@ -72,7 +72,7 @@ public: } }; -bool StatusHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool StatusHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() > 3) return false; @@ -85,8 +85,6 @@ bool StatusHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request qd.Provider = new StatusTargetProvider(); qd.Permission = "status/query"; - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - params->Set("type", "Status"); if (request.RequestUrl->GetPath().size() >= 3) diff --git a/lib/remote/statushandler.hpp b/lib/remote/statushandler.hpp index 2bef57a14..96f5c185c 100644 --- a/lib/remote/statushandler.hpp +++ b/lib/remote/statushandler.hpp @@ -30,7 +30,8 @@ class I2_REMOTE_API StatusHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(StatusHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; }; } diff --git a/lib/remote/typequeryhandler.cpp b/lib/remote/typequeryhandler.cpp index ba8e5225c..7af46510e 100644 --- a/lib/remote/typequeryhandler.cpp +++ b/lib/remote/typequeryhandler.cpp @@ -75,7 +75,7 @@ public: } }; -bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) +bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params) { if (request.RequestUrl->GetPath().size() > 3) return false; @@ -87,8 +87,6 @@ bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& requ qd.Types.insert("Type"); qd.Provider = new TypeTargetProvider(); - Dictionary::Ptr params = HttpUtility::FetchRequestParameters(request); - if (params->Contains("type")) params->Set("name", params->Get("type")); diff --git a/lib/remote/typequeryhandler.hpp b/lib/remote/typequeryhandler.hpp index fbe18f82b..67deeec35 100644 --- a/lib/remote/typequeryhandler.hpp +++ b/lib/remote/typequeryhandler.hpp @@ -30,7 +30,8 @@ class I2_REMOTE_API TypeQueryHandler : public HttpHandler public: DECLARE_PTR_TYPEDEFS(TypeQueryHandler); - virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response) override; + virtual bool HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, + HttpResponse& response, const Dictionary::Ptr& params) override; }; } From 89c7b5a90043117d1ea6625cb439037f6067ff0b Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 11 May 2016 09:23:55 +0200 Subject: [PATCH 24/42] Fix: "day -X" time specifications are parsed incorrectly fixes #11147 --- lib/icinga/legacytimeperiod.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/icinga/legacytimeperiod.cpp b/lib/icinga/legacytimeperiod.cpp index 267fb67f2..2c08394e9 100644 --- a/lib/icinga/legacytimeperiod.cpp +++ b/lib/icinga/legacytimeperiod.cpp @@ -150,6 +150,7 @@ void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end, BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid day in time specification: " + timespec)); if (begin) { + *begin = *reference; begin->tm_year = year - 1900; begin->tm_mon = month - 1; begin->tm_mday = day; @@ -159,6 +160,7 @@ void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end, } if (end) { + *end = *reference; end->tm_year = year - 1900; end->tm_mon = month - 1; end->tm_mday = day; @@ -191,7 +193,7 @@ void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end, /* Negative days are relative to the next month. */ if (mday < 0) { - end->tm_mday--; + begin->tm_mday = mday * -1 - 1; begin->tm_mon++; } } @@ -206,7 +208,7 @@ void LegacyTimePeriod::ParseTimeSpec(const String& timespec, tm *begin, tm *end, /* Negative days are relative to the next month. */ if (mday < 0) { - end->tm_mday--; + end->tm_mday = mday * -1 - 1; end->tm_mon++; } } From 88806b031b29bfa8585874b444630c90a6de37da Mon Sep 17 00:00:00 2001 From: Jason Young Date: Sun, 13 Sep 2015 21:05:32 -0400 Subject: [PATCH 25/42] Throw exception if PerfdataWriter::RotateFile fails to rename from host_temp_path to host_perfdata_path (and same for service) This can happen if the two paths are not on the same mount-point. fixes #9236 Signed-off-by: Gunnar Beutner --- lib/perfdata/perfdatawriter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/perfdata/perfdatawriter.cpp b/lib/perfdata/perfdatawriter.cpp index 57cd37673..7760045ec 100644 --- a/lib/perfdata/perfdatawriter.cpp +++ b/lib/perfdata/perfdatawriter.cpp @@ -124,7 +124,12 @@ void PerfdataWriter::RotateFile(std::ofstream& output, const String& temp_path, output.close(); String finalFile = perfdata_path + "." + Convert::ToString((long)Utility::GetTime()); - (void) rename(temp_path.CStr(), finalFile.CStr()); + if (rename(temp_path.CStr(), finalFile.CStr()) < 0) { + BOOST_THROW_EXCEPTION(posix_error() + << boost::errinfo_api_function("rename") + << boost::errinfo_errno(errno) + << boost::errinfo_file_name(temp_path)); + } } output.open(temp_path.CStr()); From 48ea8800c3bb8e0e33d99fa60d66e8cf512c6e73 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 11 May 2016 09:32:37 +0200 Subject: [PATCH 26/42] Update .mailmap for Jason Youngh refs #9236 --- .mailmap | 1 + AUTHORS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index 1fde5f02e..fec13baf8 100644 --- a/.mailmap +++ b/.mailmap @@ -11,3 +11,4 @@ Claudio Kuenzler + diff --git a/AUTHORS b/AUTHORS index 4019bbcac..c92252746 100644 --- a/AUTHORS +++ b/AUTHORS @@ -37,7 +37,7 @@ Ildar Hizbulin James Pharaoh Jan Andres Jan Wagner -Jason Young +Jason Young Jean-Marcel Flach Jesse Morgan Jo Goossens From bd896935d6b7cff4b97d1be010ca023c793b83cb Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 11 May 2016 09:48:18 +0200 Subject: [PATCH 27/42] Make sure that object names aren't empty fixes #9989 --- lib/config/configitem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index f767c4c8d..580bb97f3 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -219,6 +219,9 @@ ConfigObject::Ptr ConfigItem::Commit(bool discard) NameComposer *nc = dynamic_cast(type.get()); if (nc) { + if (name.IsEmpty()) + BOOST_THROW_EXCEPTION(ScriptError("Object name must not be empty.", m_DebugInfo)); + name = nc->MakeName(name, dobj); if (name.IsEmpty()) From 63445398c0b9f5d32ccfadf42d2351a384d1669b Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 11 May 2016 10:09:54 +0200 Subject: [PATCH 28/42] Improve error handling for TlsStream::Handshake fixes #11046 --- lib/base/tlsstream.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/base/tlsstream.cpp b/lib/base/tlsstream.cpp index 93a7501a5..c4074eaa0 100644 --- a/lib/base/tlsstream.cpp +++ b/lib/base/tlsstream.cpp @@ -268,9 +268,12 @@ void TlsStream::Handshake(void) m_CurrentAction = TlsActionHandshake; ChangeEvents(POLLOUT); - while (!m_HandshakeOK && !m_ErrorOccurred) + while (!m_HandshakeOK && !m_ErrorOccurred && !m_Eof) m_CV.wait(lock); + if (m_Eof) + BOOST_THROW_EXCEPTION(std::runtime_error("Socket was closed during TLS handshake.")); + HandleError(); } From b39634d94802f1ad0ec1feb47261a6e01dc987a2 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Mon, 9 May 2016 16:14:48 +0200 Subject: [PATCH 29/42] Fix overloaded command pipe errors fixes #11390 --- lib/compat/externalcommandlistener.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/compat/externalcommandlistener.cpp b/lib/compat/externalcommandlistener.cpp index cc484f1f6..aac24ecc5 100644 --- a/lib/compat/externalcommandlistener.cpp +++ b/lib/compat/externalcommandlistener.cpp @@ -115,13 +115,18 @@ void ExternalCommandListener::CommandPipeThread(const String& commandPath) try { rc = sock->Read(buffer, sizeof(buffer)); } catch (const std::exception& ex) { + /* We have read all data. */ + if (errno == EAGAIN) + continue; + Log(LogWarning, "ExternalCommandListener") - << "Cannot read from socket." << DiagnosticInformation(ex); + << "Cannot read from command pipe." << DiagnosticInformation(ex); break; } - if (rc <= 0) - break; + /* Empty pipe (EOF) */ + if (rc == 0) + continue; fifo->Write(buffer, rc); From 208d035db4d04d32f613e9f3c120b9058a7bda14 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 11 May 2016 12:50:08 +0200 Subject: [PATCH 30/42] Only activate HARunOnce objects once there's a cluster connection fixes #11765 --- lib/base/application.cpp | 13 +++++++++ lib/base/application.hpp | 4 +++ lib/base/configobject.cpp | 3 +- lib/cli/daemoncommand.cpp | 4 +++ lib/remote/apilistener.hpp | 3 ++ lib/remote/authority.cpp | 56 +++++++++++++++++++++++++------------- 6 files changed, 63 insertions(+), 20 deletions(-) diff --git a/lib/base/application.cpp b/lib/base/application.cpp index b0cdc1286..c9e2c7c33 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -60,6 +60,7 @@ static bool l_InExceptionHandler = false; int Application::m_ArgC; char **Application::m_ArgV; double Application::m_StartTime; +double Application::m_MainTime; bool Application::m_ScriptDebuggerEnabled = false; /** @@ -885,6 +886,8 @@ int Application::Run(void) return EXIT_FAILURE; } + SetMainTime(Utility::GetTime()); + return Main(); } @@ -1364,6 +1367,16 @@ void Application::SetStartTime(double ts) m_StartTime = ts; } +double Application::GetMainTime(void) +{ + return m_MainTime; +} + +void Application::SetMainTime(double ts) +{ + m_MainTime = ts; +} + bool Application::GetScriptDebuggerEnabled(void) { return m_ScriptDebuggerEnabled; diff --git a/lib/base/application.hpp b/lib/base/application.hpp index d030eb2a7..2174d3e03 100644 --- a/lib/base/application.hpp +++ b/lib/base/application.hpp @@ -134,6 +134,9 @@ public: static double GetStartTime(void); static void SetStartTime(double ts); + static double GetMainTime(void); + static void SetMainTime(double ts); + static bool GetScriptDebuggerEnabled(void); static void SetScriptDebuggerEnabled(bool enabled); @@ -167,6 +170,7 @@ private: static bool m_Debugging; /**< Whether debugging is enabled. */ static LogSeverity m_DebuggingSeverity; /**< Whether debugging severity is set. */ static double m_StartTime; + static double m_MainTime; static bool m_ScriptDebuggerEnabled; #ifndef _WIN32 diff --git a/lib/base/configobject.cpp b/lib/base/configobject.cpp index cfb099480..1cb6e3f3c 100644 --- a/lib/base/configobject.cpp +++ b/lib/base/configobject.cpp @@ -396,7 +396,8 @@ void ConfigObject::Activate(bool runtimeCreated) SetActive(true, true); } - SetAuthority(true); + if (GetHAMode() == HARunEverywhere) + SetAuthority(true); NotifyActive(); } diff --git a/lib/cli/daemoncommand.cpp b/lib/cli/daemoncommand.cpp index f650af3a4..be32b22d6 100644 --- a/lib/cli/daemoncommand.cpp +++ b/lib/cli/daemoncommand.cpp @@ -19,6 +19,7 @@ #include "cli/daemoncommand.hpp" #include "cli/daemonutility.hpp" +#include "remote/apilistener.hpp" #include "config/configcompiler.hpp" #include "config/configcompilercontext.hpp" #include "config/configitembuilder.hpp" @@ -302,5 +303,8 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vectorRun(); } diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index 4c860f89b..d4cb5e540 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -97,6 +97,9 @@ public: static Value ConfigDeleteObjectAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static Value HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); + + static void UpdateObjectAuthorityAsync(void); + protected: virtual void OnConfigLoaded(void) override; virtual void OnAllConfigLoaded(void) override; diff --git a/lib/remote/authority.cpp b/lib/remote/authority.cpp index 37a244ee4..cb81fa927 100644 --- a/lib/remote/authority.cpp +++ b/lib/remote/authority.cpp @@ -36,42 +36,60 @@ static bool ObjectNameLessComparer(const ConfigObject::Ptr& a, const ConfigObjec static void AuthorityTimerHandler(void) { - ApiListener::Ptr listener = ApiListener::GetInstance(); - - if (!listener || !listener->IsActive()) - return; - Zone::Ptr my_zone = Zone::GetLocalZone(); - if (!my_zone) - return; - - Endpoint::Ptr my_endpoint = Endpoint::GetLocalEndpoint(); std::vector endpoints; - BOOST_FOREACH(const Endpoint::Ptr& endpoint, my_zone->GetEndpoints()) { - if (!endpoint->GetConnected() && endpoint != my_endpoint) - continue; + Endpoint::Ptr my_endpoint; - endpoints.push_back(endpoint); + if (my_zone) { + my_endpoint = Endpoint::GetLocalEndpoint(); + + int num_total = 0; + + BOOST_FOREACH(const Endpoint::Ptr& endpoint, my_zone->GetEndpoints()) { + num_total++; + + if (endpoint != my_endpoint && !endpoint->GetConnected()) + continue; + + endpoints.push_back(endpoint); + } + + double mainTime = Application::GetMainTime(); + + if (num_total > 1 && endpoints.size() <= 1 && (mainTime == 0 || Utility::GetTime() - mainTime < 60)) + return; + + std::sort(endpoints.begin(), endpoints.end(), ObjectNameLessComparer); } - std::sort(endpoints.begin(), endpoints.end(), ObjectNameLessComparer); - BOOST_FOREACH(const ConfigType::Ptr& type, ConfigType::GetTypes()) { BOOST_FOREACH(const ConfigObject::Ptr& object, type->GetObjects()) { - Endpoint::Ptr endpoint = endpoints[Utility::SDBM(object->GetName()) % endpoints.size()]; + if (object->GetHAMode() != HARunOnce) + continue; - if (object->GetHAMode() == HARunOnce) - object->SetAuthority(endpoint == my_endpoint); + bool authority; + + if (!my_zone) + authority = true; + else + authority = endpoints[Utility::SDBM(object->GetName()) % endpoints.size()] == my_endpoint; + + object->SetAuthority(authority); } } } +void ApiListener::UpdateObjectAuthorityAsync(void) +{ + l_AuthorityTimer->Reschedule(0); +} + static void StaticInitialize(void) { l_AuthorityTimer = new Timer(); l_AuthorityTimer->OnTimerExpired.connect(boost::bind(&AuthorityTimerHandler)); - l_AuthorityTimer->SetInterval(30); + l_AuthorityTimer->SetInterval(15); l_AuthorityTimer->Start(); } From 17fa3271594b1b44bdeed2ddee2461f6d58d4742 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 11 May 2016 13:04:39 +0200 Subject: [PATCH 31/42] Improve timing behavior for UpdateObjectAuthority calls refs #11765 --- lib/cli/daemoncommand.cpp | 3 +-- lib/remote/apilistener.cpp | 3 +++ lib/remote/apilistener.hpp | 2 +- lib/remote/authority.cpp | 11 +++-------- lib/remote/configobjectutility.cpp | 3 +++ 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/cli/daemoncommand.cpp b/lib/cli/daemoncommand.cpp index be32b22d6..5d8e6f5dd 100644 --- a/lib/cli/daemoncommand.cpp +++ b/lib/cli/daemoncommand.cpp @@ -303,8 +303,7 @@ int DaemonCommand::Run(const po::variables_map& vm, const std::vectorRun(); } diff --git a/lib/remote/apilistener.cpp b/lib/remote/apilistener.cpp index 60498c7cc..14d0cdbc6 100644 --- a/lib/remote/apilistener.cpp +++ b/lib/remote/apilistener.cpp @@ -431,6 +431,9 @@ void ApiListener::SyncClient(const JsonRpcConnection::Ptr& aclient, const Endpoi ReplayLog(aclient); + if (endpoint->GetZone() == Zone::GetLocalZone()) + UpdateObjectAuthority(); + Log(LogInformation, "ApiListener") << "Finished sending replay log for endpoint '" << endpoint->GetName() << "'."; } catch (const std::exception& ex) { diff --git a/lib/remote/apilistener.hpp b/lib/remote/apilistener.hpp index d4cb5e540..895524f69 100644 --- a/lib/remote/apilistener.hpp +++ b/lib/remote/apilistener.hpp @@ -98,7 +98,7 @@ public: static Value HelloAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); - static void UpdateObjectAuthorityAsync(void); + static void UpdateObjectAuthority(void); protected: virtual void OnConfigLoaded(void) override; diff --git a/lib/remote/authority.cpp b/lib/remote/authority.cpp index cb81fa927..868ff0eba 100644 --- a/lib/remote/authority.cpp +++ b/lib/remote/authority.cpp @@ -34,7 +34,7 @@ static bool ObjectNameLessComparer(const ConfigObject::Ptr& a, const ConfigObjec return a->GetName() < b->GetName(); } -static void AuthorityTimerHandler(void) +void ApiListener::UpdateObjectAuthority(void) { Zone::Ptr my_zone = Zone::GetLocalZone(); @@ -80,16 +80,11 @@ static void AuthorityTimerHandler(void) } } -void ApiListener::UpdateObjectAuthorityAsync(void) -{ - l_AuthorityTimer->Reschedule(0); -} - static void StaticInitialize(void) { l_AuthorityTimer = new Timer(); - l_AuthorityTimer->OnTimerExpired.connect(boost::bind(&AuthorityTimerHandler)); - l_AuthorityTimer->SetInterval(15); + l_AuthorityTimer->OnTimerExpired.connect(boost::bind(&ApiListener::UpdateObjectAuthority)); + l_AuthorityTimer->SetInterval(30); l_AuthorityTimer->Start(); } diff --git a/lib/remote/configobjectutility.cpp b/lib/remote/configobjectutility.cpp index 0656aac79..107caa4d1 100644 --- a/lib/remote/configobjectutility.cpp +++ b/lib/remote/configobjectutility.cpp @@ -19,6 +19,7 @@ #include "remote/configobjectutility.hpp" #include "remote/configpackageutility.hpp" +#include "remote/apilistener.hpp" #include "config/configcompiler.hpp" #include "config/configitem.hpp" #include "base/configwriter.hpp" @@ -149,6 +150,8 @@ bool ConfigObjectUtility::CreateObject(const Type::Ptr& type, const String& full return false; } + + ApiListener::UpdateObjectAuthority(); } catch (const std::exception& ex) { delete expr; From 40b4040880c3fd50c8ad993021f9af3c4fa2fcab Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Wed, 11 May 2016 14:03:40 +0200 Subject: [PATCH 32/42] Ensure that program status updates are immediately updated in DB IDO fixes #11767 --- lib/db_ido/dbconnection.cpp | 22 +++++++++++++--------- lib/db_ido/dbconnection.hpp | 3 ++- lib/db_ido/dbquery.hpp | 3 ++- lib/db_ido_mysql/idomysqlconnection.cpp | 14 +++++++++++--- lib/db_ido_pgsql/idopgsqlconnection.cpp | 14 +++++++++++--- 5 files changed, 39 insertions(+), 17 deletions(-) diff --git a/lib/db_ido/dbconnection.cpp b/lib/db_ido/dbconnection.cpp index 694b9e37f..c7995c8f2 100644 --- a/lib/db_ido/dbconnection.cpp +++ b/lib/db_ido/dbconnection.cpp @@ -146,7 +146,7 @@ void DbConnection::InitializeDbTimer(void) { m_ProgramStatusTimer = new Timer(); m_ProgramStatusTimer->SetInterval(10); - m_ProgramStatusTimer->OnTimerExpired.connect(boost::bind(&DbConnection::ProgramStatusHandler)); + m_ProgramStatusTimer->OnTimerExpired.connect(boost::bind(&DbConnection::UpdateProgramStatus)); m_ProgramStatusTimer->Start(); } @@ -163,7 +163,7 @@ void DbConnection::InsertRuntimeVariable(const String& key, const Value& value) DbObject::OnQuery(query); } -void DbConnection::ProgramStatusHandler(void) +void DbConnection::UpdateProgramStatus(void) { Log(LogNotice, "DbConnection") << "Updating programstatus table."; @@ -206,15 +206,19 @@ void DbConnection::ProgramStatusHandler(void) query2.Priority = PriorityHigh; queries.push_back(query2); + DbQuery query3; + query3.Type = DbQueryNewTransaction; + queries.push_back(query3); + DbObject::OnMultipleQueries(queries); - DbQuery query3; - query3.Table = "runtimevariables"; - query3.Type = DbQueryDelete; - query3.Category = DbCatProgramStatus; - query3.WhereCriteria = new Dictionary(); - query3.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */ - DbObject::OnQuery(query3); + DbQuery query4; + query4.Table = "runtimevariables"; + query4.Type = DbQueryDelete; + query4.Category = DbCatProgramStatus; + query4.WhereCriteria = new Dictionary(); + query4.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */ + DbObject::OnQuery(query4); InsertRuntimeVariable("total_services", std::distance(ConfigType::GetObjectsByType().first, ConfigType::GetObjectsByType().second)); InsertRuntimeVariable("total_scheduled_services", std::distance(ConfigType::GetObjectsByType().first, ConfigType::GetObjectsByType().second)); diff --git a/lib/db_ido/dbconnection.hpp b/lib/db_ido/dbconnection.hpp index 6f81178f7..94b1af485 100644 --- a/lib/db_ido/dbconnection.hpp +++ b/lib/db_ido/dbconnection.hpp @@ -95,6 +95,8 @@ protected: void IncreaseQueryCount(void); + static void UpdateProgramStatus(void); + private: std::map m_ObjectIDs; std::map, DbReference> m_InsertIDs; @@ -114,7 +116,6 @@ private: void StatsLoggerTimerHandler(void); static void InsertRuntimeVariable(const String& key, const Value& value); - static void ProgramStatusHandler(void); mutable boost::mutex m_StatsMutex; RingBuffer m_QueryStats; diff --git a/lib/db_ido/dbquery.hpp b/lib/db_ido/dbquery.hpp index 1dd2d4318..3161d1c1b 100644 --- a/lib/db_ido/dbquery.hpp +++ b/lib/db_ido/dbquery.hpp @@ -33,7 +33,8 @@ enum DbQueryType { DbQueryInsert = 1, DbQueryUpdate = 2, - DbQueryDelete = 4 + DbQueryDelete = 4, + DbQueryNewTransaction = 8 }; enum DbQueryCategory diff --git a/lib/db_ido_mysql/idomysqlconnection.cpp b/lib/db_ido_mysql/idomysqlconnection.cpp index 56c2767a8..a71e7b389 100644 --- a/lib/db_ido_mysql/idomysqlconnection.cpp +++ b/lib/db_ido_mysql/idomysqlconnection.cpp @@ -337,6 +337,9 @@ void IdoMysqlConnection::Reconnect(void) /* set session time zone to utc */ Query("SET SESSION TIME_ZONE='+00:00'"); + /* update programstatus table */ + UpdateProgramStatus(); + /* record connection */ Query("INSERT INTO " + GetTablePrefix() + "conninfo " + "(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES (" @@ -818,7 +821,7 @@ void IdoMysqlConnection::InternalExecuteMultipleQueries(const std::vectorGetObject()->GetExtension("agent_check").ToBool()) diff --git a/lib/db_ido_pgsql/idopgsqlconnection.cpp b/lib/db_ido_pgsql/idopgsqlconnection.cpp index a55f5fa64..93f72f9d1 100644 --- a/lib/db_ido_pgsql/idopgsqlconnection.cpp +++ b/lib/db_ido_pgsql/idopgsqlconnection.cpp @@ -324,6 +324,9 @@ void IdoPgsqlConnection::Reconnect(void) Log(LogInformation, "IdoPgsqlConnection") << "pgSQL IDO instance id: " << static_cast(m_InstanceID) << " (schema version: '" + version + "')"; + /* update programstatus table */ + UpdateProgramStatus(); + /* record connection */ Query("INSERT INTO " + GetTablePrefix() + "conninfo " + "(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES (" @@ -697,7 +700,7 @@ void IdoPgsqlConnection::InternalExecuteMultipleQueries(const std::vectorGetObject()->GetExtension("agent_check").ToBool()) From 21d1ffa28bac749cfe7ebdf48fbccd7aa5387b23 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 11 May 2016 14:13:05 +0200 Subject: [PATCH 33/42] Fix incorrect calculation in Downtime::IsInEffect fixes #11589 --- lib/icinga/downtime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/icinga/downtime.cpp b/lib/icinga/downtime.cpp index 62bdb7c71..eaa2e9ba2 100644 --- a/lib/icinga/downtime.cpp +++ b/lib/icinga/downtime.cpp @@ -166,7 +166,7 @@ bool Downtime::IsInEffect(void) const if (triggerTime == 0) return false; - return (triggerTime + GetDuration() < now); + return (now < triggerTime + GetDuration()); } bool Downtime::IsTriggered(void) const From ff24863a72bba0d136995c48c423e90427322772 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 11 May 2016 16:07:28 +0200 Subject: [PATCH 34/42] Report failed reload attempts for the icinga check fixes #9060 fixes #9997 fixes #11129 --- lib/base/application.cpp | 15 ++++++++++++++- lib/base/application.hpp | 4 ++++ lib/methods/icingachecktask.cpp | 9 ++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/base/application.cpp b/lib/base/application.cpp index c9e2c7c33..77c610216 100644 --- a/lib/base/application.cpp +++ b/lib/base/application.cpp @@ -62,6 +62,7 @@ char **Application::m_ArgV; double Application::m_StartTime; double Application::m_MainTime; bool Application::m_ScriptDebuggerEnabled = false; +double Application::m_LastReloadFailed; /** * Constructor for the Application class. @@ -330,8 +331,10 @@ void Application::OnShutdown(void) static void ReloadProcessCallbackInternal(const ProcessResult& pr) { - if (pr.ExitStatus != 0) + if (pr.ExitStatus != 0) { + Application::SetLastReloadFailed(Utility::GetTime()); Log(LogCritical, "Application", "Found error in config: reloading aborted"); + } #ifdef _WIN32 else Application::Exit(7); /* keep this exit code in sync with icinga-app */ @@ -1387,6 +1390,16 @@ void Application::SetScriptDebuggerEnabled(bool enabled) m_ScriptDebuggerEnabled = enabled; } +double Application::GetLastReloadFailed(void) +{ + return m_LastReloadFailed; +} + +void Application::SetLastReloadFailed(double ts) +{ + m_LastReloadFailed = ts; +} + void Application::ValidateName(const String& value, const ValidationUtils& utils) { ObjectImpl::ValidateName(value, utils); diff --git a/lib/base/application.hpp b/lib/base/application.hpp index 2174d3e03..9e5bb3d14 100644 --- a/lib/base/application.hpp +++ b/lib/base/application.hpp @@ -140,6 +140,9 @@ public: static bool GetScriptDebuggerEnabled(void); static void SetScriptDebuggerEnabled(bool enabled); + static double GetLastReloadFailed(void); + static void SetLastReloadFailed(double ts); + static void DisplayInfoMessage(std::ostream& os, bool skipVersion = false); protected: @@ -172,6 +175,7 @@ private: static double m_StartTime; static double m_MainTime; static bool m_ScriptDebuggerEnabled; + static double m_LastReloadFailed; #ifndef _WIN32 static void SigIntTermHandler(int signum); diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp index 9ce8797f0..89642a39d 100644 --- a/lib/methods/icingachecktask.cpp +++ b/lib/methods/icingachecktask.cpp @@ -100,7 +100,14 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& service, const CheckResul cr->SetOutput("Icinga 2 has been running for " + Utility::FormatDuration(uptime) + ". Version: " + Application::GetAppVersion()); cr->SetPerformanceData(perfdata); - cr->SetState(ServiceOK); + + double lastReloadFailed = Application::GetLastReloadFailed(); + + if (lastReloadFailed > 0) { + cr->SetOutput(cr->GetOutput() + "; Last reload attempt failed at " + Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", lastReloadFailed)); + cr->SetState(ServiceWarning); + } else + cr->SetState(ServiceOK); service->ProcessCheckResult(cr); } From a6e70a155dc977090364b35936ce72ecfc7cab4b Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Wed, 11 May 2016 16:12:09 +0200 Subject: [PATCH 35/42] Fix possible crash in Add{Comment,Downtime} refs #11112 --- lib/compat/externalcommandlistener.cpp | 2 +- lib/icinga/comment.cpp | 3 +++ lib/icinga/downtime.cpp | 4 +++- lib/icinga/externalcommandprocessor.cpp | 4 ++-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/compat/externalcommandlistener.cpp b/lib/compat/externalcommandlistener.cpp index aac24ecc5..dcd57eb3f 100644 --- a/lib/compat/externalcommandlistener.cpp +++ b/lib/compat/externalcommandlistener.cpp @@ -144,7 +144,7 @@ void ExternalCommandListener::CommandPipeThread(const String& commandPath) ExternalCommandProcessor::Execute(command); } catch (const std::exception& ex) { Log(LogWarning, "ExternalCommandListener") - << "External command failed." << DiagnosticInformation(ex); + << "External command failed: " << DiagnosticInformation(ex); } } } diff --git a/lib/icinga/comment.cpp b/lib/icinga/comment.cpp index eeb7b2b60..fd98ae120 100644 --- a/lib/icinga/comment.cpp +++ b/lib/icinga/comment.cpp @@ -195,6 +195,9 @@ String Comment::AddComment(const Checkable::Ptr& checkable, CommentType entryTyp Comment::Ptr comment = Comment::GetByName(fullName); + if (!comment) + BOOST_THROW_EXCEPTION(std::runtime_error("Could not create comment.")); + Log(LogNotice, "Comment") << "Added comment '" << comment->GetName() << "'."; diff --git a/lib/icinga/downtime.cpp b/lib/icinga/downtime.cpp index eaa2e9ba2..66b6a635e 100644 --- a/lib/icinga/downtime.cpp +++ b/lib/icinga/downtime.cpp @@ -251,12 +251,14 @@ String Downtime::AddDowntime(const Checkable::Ptr& checkable, const String& auth Downtime::Ptr downtime = Downtime::GetByName(fullName); + if (!downtime) + BOOST_THROW_EXCEPTION(std::runtime_error("Could not create downtime.")); + Log(LogNotice, "Downtime") << "Added downtime '" << downtime->GetName() << "' between '" << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", startTime) << "' and '" << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", endTime) << "'."; - return fullName; } diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index 6d1711d08..c9c921c41 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -1210,7 +1210,7 @@ void ExternalCommandProcessor::AddHostComment(double, const std::vector& BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot add host comment for non-existent host '" + arguments[0] + "'")); if (arguments[2].IsEmpty() || arguments[3].IsEmpty()) - BOOST_THROW_EXCEPTION(std::invalid_argument("Author and comment may not be empty")); + BOOST_THROW_EXCEPTION(std::invalid_argument("Author and comment must not be empty")); Log(LogNotice, "ExternalCommandProcessor") << "Creating comment for host " << host->GetName(); @@ -1234,7 +1234,7 @@ void ExternalCommandProcessor::AddSvcComment(double, const std::vector& BOOST_THROW_EXCEPTION(std::invalid_argument("Cannot add service comment for non-existent service '" + arguments[1] + "' on host '" + arguments[0] + "'")); if (arguments[3].IsEmpty() || arguments[4].IsEmpty()) - BOOST_THROW_EXCEPTION(std::invalid_argument("Author and comment may not be empty")); + BOOST_THROW_EXCEPTION(std::invalid_argument("Author and comment must not be empty")); Log(LogNotice, "ExternalCommandProcessor") << "Creating comment for service " << service->GetName(); From 02af5e2df3ae81371a47234646124c901bb356e1 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Wed, 11 May 2016 16:52:52 +0200 Subject: [PATCH 36/42] Add the category to the generated changelog fixes #11768 --- changelog.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/changelog.py b/changelog.py index 8ad0a0155..64c8cb58a 100755 --- a/changelog.py +++ b/changelog.py @@ -43,14 +43,14 @@ def format_header(text, lvl, ftype = ftype): def format_logentry(log_entry, args = args, issue_url = ISSUE_URL): if args.links: if args.html: - return "
  • {0} {1}: {2}
  • ".format(log_entry[0], log_entry[1], log_entry[2], issue_url) + return "
  • {0} {1} ({2}): {3}
  • ".format(log_entry[0], log_entry[1], log_entry[2], log_entry[3],issue_url) else: - return "* {0} [{1}]({3}{1} \"{0} {1}\"): {2}".format(log_entry[0], log_entry[1], log_entry[2], issue_url) + return "* {0} [{1}]({4}{1} \"{0} {1}\") ({2}): {3}".format(log_entry[0], log_entry[1], log_entry[2], log_entry[3], issue_url) else: if args.html: - return "
  • %s %d: %s
  • " % log_entry + return "
  • %s %d (%s): %s
  • " % log_entry else: - return "* %s %d: %s" % log_entry + return "* %s %d (%s): %s" % log_entry def print_category(category, entries): if len(entries) > 0: @@ -135,7 +135,13 @@ while True: if ignore_issue: continue - entry = (issue["tracker"]["name"], issue["id"], issue["subject"].strip()) + if "category" in issue: + category = issue["category"]["name"] + else: + category = "no category" + + # the order is important for print_category() + entry = (issue["tracker"]["name"], issue["id"], category, issue["subject"].strip()) if issue["tracker"]["name"] == "Feature": features.append(entry) From 1f2fe64220566d27ba41501252bec55ccfd0995e Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 12 May 2016 10:24:15 +0200 Subject: [PATCH 37/42] Fix incorrect variable names for joined fields in filter expressions fixes #11779 --- lib/remote/filterutility.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/remote/filterutility.cpp b/lib/remote/filterutility.cpp index d5e8cd1b0..d9f50e7bc 100644 --- a/lib/remote/filterutility.cpp +++ b/lib/remote/filterutility.cpp @@ -123,7 +123,10 @@ bool FilterUtility::EvaluateFilter(ScriptFrame& frame, Expression *filter, Object::Ptr joinedObj = target->NavigateField(fid); - vars->Set(field.Name, joinedObj); + if (field.NavigationName) + vars->Set(field.NavigationName, joinedObj); + else + vars->Set(field.Name, joinedObj); } return Convert::ToBool(filter->Evaluate(frame)); From 693358f790e4b94549e13c97688b7fde1bbb8d1c Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Thu, 12 May 2016 10:57:51 +0200 Subject: [PATCH 38/42] Update documentation for /v1/actions/remove-downtime refs #11779 --- doc/9-icinga2-api.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/doc/9-icinga2-api.md b/doc/9-icinga2-api.md index b10853e4b..eca7efff0 100644 --- a/doc/9-icinga2-api.md +++ b/doc/9-icinga2-api.md @@ -980,6 +980,28 @@ Example for removing all host downtimes using a host name filter for `example.lo ] } +Example for removing a downtime from a host but not the services filtered by the author name. This example uses +filter variables explained in the [advanced filters](9-icinga2-api.md#icinga2-api-advanced-filters) chapter. + + $ curl -k -s -u root:icinga -H 'Accept: application/json' -X POST 'https://localhost:5665/v1/actions/remove-downtime' \ + -d $'{ + "type": "Downtime", + "filter": "host.name == filterHost && !service && downtime.author == filterAuthor", + "filter_vars": { + "filterHost": "example.localdomain", + "filterAuthor": "icingaadmin" + } + }' | python -m json.tool + + { + "results": [ + { + "code": 200.0, + "status": "Successfully removed downtime 'example.localdomain!mbmif.local-1463043129-3'." + } + ] + } + ### shutdown-process Shuts down Icinga2. May or may not return. From 01e58b463ac1f7f328be221dd6efca31cffec622 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 12 May 2016 13:46:22 +0200 Subject: [PATCH 39/42] Fix incorrect re-scheduling behavior for command_endpoint checks refs #8137 --- lib/checker/checkercomponent.cpp | 48 +++++++++++++++----------------- lib/checker/checkercomponent.hpp | 1 - lib/icinga/checkable-check.cpp | 21 ++++++++++++++ lib/icinga/checkable.hpp | 7 +++++ lib/methods/pluginchecktask.cpp | 4 +++ 5 files changed, 54 insertions(+), 27 deletions(-) diff --git a/lib/checker/checkercomponent.cpp b/lib/checker/checkercomponent.cpp index 6b20cc471..fb9d87188 100644 --- a/lib/checker/checkercomponent.cpp +++ b/lib/checker/checkercomponent.cpp @@ -69,7 +69,6 @@ void CheckerComponent::OnConfigLoaded(void) ConfigObject::OnActiveChanged.connect(bind(&CheckerComponent::ObjectHandler, this, _1)); ConfigObject::OnPausedChanged.connect(bind(&CheckerComponent::ObjectHandler, this, _1)); - Checkable::OnNewCheckResult.connect(bind(&CheckerComponent::CheckResultHandler, this, _1)); Checkable::OnNextCheckChanged.connect(bind(&CheckerComponent::NextCheckChangedHandler, this, _1)); } @@ -122,7 +121,7 @@ void CheckerComponent::CheckThreadProc(void) double wait = checkable->GetNextCheck() - Utility::GetTime(); - if (wait > 0 || m_PendingCheckables.size() >= GetConcurrentChecks()) { + if (wait > 0 || Checkable::GetPendingChecks() >= GetConcurrentChecks()) { /* Wait for the next check. */ m_CV.timed_wait(lock, boost::posix_time::milliseconds(wait * 1000)); @@ -216,6 +215,27 @@ void CheckerComponent::ExecuteCheckHelper(const Checkable::Ptr& checkable) Log(LogCritical, "checker", output); } + + { + boost::mutex::scoped_lock lock(m_Mutex); + + /* remove the object from the list of pending objects; if it's not in the + * list this was a manual (i.e. forced) check and we must not re-add the + * object to the list because it's already there. */ + CheckerComponent::CheckableSet::iterator it; + it = m_PendingCheckables.find(checkable); + if (it != m_PendingCheckables.end()) { + m_PendingCheckables.erase(it); + + if (checkable->IsActive()) + m_IdleCheckables.insert(checkable); + + m_CV.notify_all(); + } + } + + Log(LogDebug, "CheckerComponent") + << "Check finished for object '" << checkable->GetName() << "'"; } void CheckerComponent::ResultTimerHandler(void) @@ -259,30 +279,6 @@ void CheckerComponent::ObjectHandler(const ConfigObject::Ptr& object) } } -void CheckerComponent::CheckResultHandler(const Checkable::Ptr& checkable) -{ - { - boost::mutex::scoped_lock lock(m_Mutex); - - /* remove the object from the list of pending objects; if it's not in the - * list this was a manual (i.e. forced) check and we must not re-add the - * object to the list because it's already there. */ - CheckerComponent::CheckableSet::iterator it; - it = m_PendingCheckables.find(checkable); - if (it != m_PendingCheckables.end()) { - m_PendingCheckables.erase(it); - - if (checkable->IsActive()) - m_IdleCheckables.insert(checkable); - - m_CV.notify_all(); - } - } - - Log(LogDebug, "CheckerComponent") - << "Check finished for object '" << checkable->GetName() << "'"; -} - void CheckerComponent::NextCheckChangedHandler(const Checkable::Ptr& checkable) { boost::mutex::scoped_lock lock(m_Mutex); diff --git a/lib/checker/checkercomponent.hpp b/lib/checker/checkercomponent.hpp index f132fe16c..989668523 100644 --- a/lib/checker/checkercomponent.hpp +++ b/lib/checker/checkercomponent.hpp @@ -97,7 +97,6 @@ private: void AdjustCheckTimer(void); void ObjectHandler(const ConfigObject::Ptr& object); - void CheckResultHandler(const Checkable::Ptr& checkable); void NextCheckChangedHandler(const Checkable::Ptr& checkable); void RescheduleCheckTimer(void); diff --git a/lib/icinga/checkable-check.cpp b/lib/icinga/checkable-check.cpp index f23cee7ed..165fd56f7 100644 --- a/lib/icinga/checkable-check.cpp +++ b/lib/icinga/checkable-check.cpp @@ -41,6 +41,9 @@ boost::signals2::signal Checkable::OnNotificationsRequested; boost::signals2::signal Checkable::OnNextCheckUpdated; +boost::mutex Checkable::m_StatsMutex; +int Checkable::m_PendingChecks = 0; + CheckCommand::Ptr Checkable::GetCheckCommand(void) const { return dynamic_pointer_cast(NavigateCheckCommandRaw()); @@ -515,3 +518,21 @@ void Checkable::UpdateStatistics(const CheckResult::Ptr& cr, CheckableType type) Log(LogWarning, "Checkable", "Unknown checkable type for statistic update."); } } + +void Checkable::IncreasePendingChecks(void) +{ + boost::mutex::scoped_lock lock(m_StatsMutex); + m_PendingChecks++; +} + +void Checkable::DecreasePendingChecks(void) +{ + boost::mutex::scoped_lock lock(m_StatsMutex); + m_PendingChecks--; +} + +int Checkable::GetPendingChecks(void) +{ + boost::mutex::scoped_lock lock(m_StatsMutex); + return m_PendingChecks; +} diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index abd966dfd..9abc19afe 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -178,6 +178,10 @@ public: virtual void ValidateCheckInterval(double value, const ValidationUtils& utils) override; + static void IncreasePendingChecks(void); + static void DecreasePendingChecks(void); + static int GetPendingChecks(void); + protected: virtual void Start(bool runtimeCreated) override; @@ -186,6 +190,9 @@ private: bool m_CheckRunning; long m_SchedulingOffset; + static boost::mutex m_StatsMutex; + static int m_PendingChecks; + /* Downtimes */ std::set m_Downtimes; mutable boost::mutex m_DowntimeMutex; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index ec8b882e6..c1b1942d9 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -52,6 +52,8 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes resolvers.push_back(std::make_pair("command", commandObj)); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); + Checkable::IncreasePendingChecks(); + PluginUtility::ExecuteCommand(commandObj, checkable, checkable->GetLastCheckResult(), resolvers, resolvedMacros, useResolvedMacros, boost::bind(&PluginCheckTask::ProcessFinishedHandler, checkable, cr, _1, _2)); @@ -59,6 +61,8 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes void PluginCheckTask::ProcessFinishedHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const Value& commandLine, const ProcessResult& pr) { + Checkable::DecreasePendingChecks(); + if (pr.ExitStatus > 3) { Process::Arguments parguments = Process::PrepareCommand(commandLine); Log(LogWarning, "PluginCheckTask") From 57149ff89998e0613956387f2730f5d1a2cf0362 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 12 May 2016 14:00:19 +0200 Subject: [PATCH 40/42] Fix race condition in ConfigObject::SetAuthority fixes #11784 --- lib/base/configobject.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/base/configobject.cpp b/lib/base/configobject.cpp index 1cb6e3f3c..560e8a58a 100644 --- a/lib/base/configobject.cpp +++ b/lib/base/configobject.cpp @@ -386,18 +386,18 @@ void ConfigObject::Activate(bool runtimeCreated) { CONTEXT("Activating object '" + GetName() + "' of type '" + GetType()->GetName() + "'"); - Start(runtimeCreated); - - ASSERT(GetStartCalled()); - { ObjectLock olock(this); + + Start(runtimeCreated); + + ASSERT(GetStartCalled()); ASSERT(!IsActive()); SetActive(true, true); - } - if (GetHAMode() == HARunEverywhere) - SetAuthority(true); + if (GetHAMode() == HARunEverywhere) + SetAuthority(true); + } NotifyActive(); } @@ -422,12 +422,12 @@ void ConfigObject::Deactivate(bool runtimeRemoved) return; SetActive(false, true); + + SetAuthority(false); + + Stop(runtimeRemoved); } - SetAuthority(false); - - Stop(runtimeRemoved); - ASSERT(GetStopCalled()); NotifyActive(); @@ -465,6 +465,8 @@ void ConfigObject::Resume(void) void ConfigObject::SetAuthority(bool authority) { + ObjectLock olock(this); + if (authority && GetPaused()) { SetResumeCalled(false); Resume(); From 49558420f9edda30b5b16bd913149c570208f439 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Thu, 12 May 2016 11:49:08 +0200 Subject: [PATCH 41/42] Release version 2.4.8 --- ChangeLog | 45 +++++++++++++++++++++++++++++++++++++ RELEASE.md | 14 ++++++------ doc/1-about.md | 45 +++++++++++++++++++++++++++++++++++++ icinga2.nuspec | 2 +- icinga2.spec | 2 +- tools/chocolateyInstall.ps1 | 4 ++-- 6 files changed, 101 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9d6410925..fd67558e9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,51 @@ Please check [doc/1-about.md]. ## What's New +### What's New in Version 2.4.8 + +#### Changes + +* Bugfixes +* Support for limiting the maximum number of concurrent checks (new configuration option) +* HA-aware features now wait for connected cluster nodes in the same zone (e.g. DB IDO) +* The 'icinga' check now alerts on failed reloads + +#### Feature + +* Feature 8137 (Checker): Maximum concurrent service checks +* Feature 9236 (Perfdata): PerfdataWriter: Better failure handling for file renames across file systems +* Feature 9997 (libmethods): "icinga" check should have state WARNING when the last reload failed +* Feature 10581 (ITL): Provide icingacli in the ITL +* Feature 11556 (libbase): Add support for subjectAltName in SSL certificates +* Feature 11651 (CLI): Implement SNI support for the CLI commands +* Feature 11720 (ITL): 'disk' CheckCommand: Exclude 'cgroup' and 'tracefs' by default +* Feature 11748 (Cluster): Remove unused cluster commands +* Feature 11765 (Cluster): Only activate HARunOnce objects once there's a cluster connection +* Feature 11768 (Documentation): Add the category to the generated changelog + +#### Bugfixes + +* Bug 9989 (Configuration): Service apply without name possible +* Bug 10426 (libicinga): Icinga crashes with a segfault on receiving a lot of check results for nonexisting hosts/services +* Bug 10717 (Configuration): Comments and downtimes of deleted checkable objects are not deleted +* Bug 11046 (Cluster): Icinga2 agent gets stuck after disconnect and won't relay messages +* Bug 11112 (Compat): Empty author/text attribute for comment/downtimes external commands causing crash +* Bug 11147 (libicinga): "day -X" time specifications are parsed incorrectly +* Bug 11158 (libicinga): Crash with empty ScheduledDowntime 'ranges' attribute +* Bug 11374 (API): Icinga2 API: deleting service with cascade=1 does not delete dependant notification +* Bug 11390 (Compat): Command pipe overloaded: Can't send external Icinga command to the local command file +* Bug 11396 (API): inconsistent API /v1/objects/* response for PUT requests +* Bug 11589 (libicinga): notification sent out during flexible downtime +* Bug 11645 (Documentation): Incorrect chapter headings for Object#to_string and Object#type +* Bug 11646 (Configuration): Wrong log severity causes segfault +* Bug 11686 (API): Icinga Crash with the workflow Create_Host-> Downtime for the Host -> Delete Downtime -> Remove Host +* Bug 11711 (libicinga): Expired downtimes are not removed +* Bug 11714 (libbase): Crash in UnameHelper +* Bug 11742 (Documentation): Missing documentation for event commands w/ execution bridge +* Bug 11757 (API): API: Missing error handling for invalid JSON request body +* Bug 11767 (DB IDO): Ensure that program status updates are immediately updated in DB IDO +* Bug 11779 (API): Incorrect variable names for joined fields in filters + ### What's New in Version 2.4.7 #### Bugfixes diff --git a/RELEASE.md b/RELEASE.md index b8c2c6ca2..da10b1b64 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -12,9 +12,9 @@ Check the following issue filters: ## Backport Commits $ git checkout master - $ ./pick.py -V 2.4.7 + $ ./pick.py -V 2.4.8 -The script creates a new branch 'auto-merged-2.4.7' which is based on the +The script creates a new branch 'auto-merged-2.4.8' which is based on the current support branch. It then merges all commits from the 'master' branch which reference a ticket for the version that was specified. @@ -26,7 +26,7 @@ rebase until no commits are left: After finishing the rebase the branch needs to be merged into the support branch: $ git checkout support/2.4 - $ git merge --ff-only auto-merged-2.4.7 + $ git merge --ff-only auto-merged-2.4.8 ## Authors @@ -50,15 +50,15 @@ the changelog.py script. Also generate HTML for the wordpress release announceme Changelog: - $ ./changelog.py -V 2.4.7 + $ ./changelog.py -V 2.4.8 Docs: - $ ./changelog.py -V 2.4.7 -l + $ ./changelog.py -V 2.4.8 -l Wordpress: - $ ./changelog.py -V 2.4.7 -H -l + $ ./changelog.py -V 2.4.8 -H -l ## Git Tag @@ -141,7 +141,7 @@ Create the nupkg package: Install the created icinga2 package locally: - choco install icinga2 -version 2.4.7 -fdv "%cd%" -source "'%cd%;https://chocolatey.org/api/v2/'" + choco install icinga2 -version 2.4.8 -fdv "%cd%" -source "'%cd%;https://chocolatey.org/api/v2/'" Upload the package to [chocolatey](https://chocolatey.org/packages/upload). diff --git a/doc/1-about.md b/doc/1-about.md index ee9e02b31..a8e8ad8b8 100644 --- a/doc/1-about.md +++ b/doc/1-about.md @@ -54,6 +54,51 @@ More details in the [Icinga FAQ](https://www.icinga.org/icinga/faq/). ## What's New +### What's New in Version 2.4.8 + +#### Changes + +* Bugfixes +* Support for limiting the maximum number of concurrent checks (new configuration option) +* HA-aware features now wait for connected cluster nodes in the same zone (e.g. DB IDO) +* The 'icinga' check now alerts on failed reloads + +#### Feature + +* Feature [8137](https://dev.icinga.org/issues/8137 "Feature 8137") (Checker): Maximum concurrent service checks +* Feature [9236](https://dev.icinga.org/issues/9236 "Feature 9236") (Perfdata): PerfdataWriter: Better failure handling for file renames across file systems +* Feature [9997](https://dev.icinga.org/issues/9997 "Feature 9997") (libmethods): "icinga" check should have state WARNING when the last reload failed +* Feature [10581](https://dev.icinga.org/issues/10581 "Feature 10581") (ITL): Provide icingacli in the ITL +* Feature [11556](https://dev.icinga.org/issues/11556 "Feature 11556") (libbase): Add support for subjectAltName in SSL certificates +* Feature [11651](https://dev.icinga.org/issues/11651 "Feature 11651") (CLI): Implement SNI support for the CLI commands +* Feature [11720](https://dev.icinga.org/issues/11720 "Feature 11720") (ITL): 'disk' CheckCommand: Exclude 'cgroup' and 'tracefs' by default +* Feature [11748](https://dev.icinga.org/issues/11748 "Feature 11748") (Cluster): Remove unused cluster commands +* Feature [11765](https://dev.icinga.org/issues/11765 "Feature 11765") (Cluster): Only activate HARunOnce objects once there's a cluster connection +* Feature [11768](https://dev.icinga.org/issues/11768 "Feature 11768") (Documentation): Add the category to the generated changelog + +#### Bugfixes + +* Bug [9989](https://dev.icinga.org/issues/9989 "Bug 9989") (Configuration): Service apply without name possible +* Bug [10426](https://dev.icinga.org/issues/10426 "Bug 10426") (libicinga): Icinga crashes with a segfault on receiving a lot of check results for nonexisting hosts/services +* Bug [10717](https://dev.icinga.org/issues/10717 "Bug 10717") (Configuration): Comments and downtimes of deleted checkable objects are not deleted +* Bug [11046](https://dev.icinga.org/issues/11046 "Bug 11046") (Cluster): Icinga2 agent gets stuck after disconnect and won't relay messages +* Bug [11112](https://dev.icinga.org/issues/11112 "Bug 11112") (Compat): Empty author/text attribute for comment/downtimes external commands causing crash +* Bug [11147](https://dev.icinga.org/issues/11147 "Bug 11147") (libicinga): "day -X" time specifications are parsed incorrectly +* Bug [11158](https://dev.icinga.org/issues/11158 "Bug 11158") (libicinga): Crash with empty ScheduledDowntime 'ranges' attribute +* Bug [11374](https://dev.icinga.org/issues/11374 "Bug 11374") (API): Icinga2 API: deleting service with cascade=1 does not delete dependant notification +* Bug [11390](https://dev.icinga.org/issues/11390 "Bug 11390") (Compat): Command pipe overloaded: Can't send external Icinga command to the local command file +* Bug [11396](https://dev.icinga.org/issues/11396 "Bug 11396") (API): inconsistent API /v1/objects/* response for PUT requests +* Bug [11589](https://dev.icinga.org/issues/11589 "Bug 11589") (libicinga): notification sent out during flexible downtime +* Bug [11645](https://dev.icinga.org/issues/11645 "Bug 11645") (Documentation): Incorrect chapter headings for Object#to_string and Object#type +* Bug [11646](https://dev.icinga.org/issues/11646 "Bug 11646") (Configuration): Wrong log severity causes segfault +* Bug [11686](https://dev.icinga.org/issues/11686 "Bug 11686") (API): Icinga Crash with the workflow Create_Host-> Downtime for the Host -> Delete Downtime -> Remove Host +* Bug [11711](https://dev.icinga.org/issues/11711 "Bug 11711") (libicinga): Expired downtimes are not removed +* Bug [11714](https://dev.icinga.org/issues/11714 "Bug 11714") (libbase): Crash in UnameHelper +* Bug [11742](https://dev.icinga.org/issues/11742 "Bug 11742") (Documentation): Missing documentation for event commands w/ execution bridge +* Bug [11757](https://dev.icinga.org/issues/11757 "Bug 11757") (API): API: Missing error handling for invalid JSON request body +* Bug [11767](https://dev.icinga.org/issues/11767 "Bug 11767") (DB IDO): Ensure that program status updates are immediately updated in DB IDO +* Bug [11779](https://dev.icinga.org/issues/11779 "Bug 11779") (API): Incorrect variable names for joined fields in filters + ### What's New in Version 2.4.7 #### Bugfixes diff --git a/icinga2.nuspec b/icinga2.nuspec index 45398aba5..416a51b39 100755 --- a/icinga2.nuspec +++ b/icinga2.nuspec @@ -6,7 +6,7 @@ icinga2 Icinga2 - 2.4.7 + 2.4.8 2016 - The Icinga Project Icinga Development Team icinga2 - Monitoring Agent for Windows diff --git a/icinga2.spec b/icinga2.spec index 3f6c980ba..445a6f9a2 100644 --- a/icinga2.spec +++ b/icinga2.spec @@ -66,7 +66,7 @@ Summary: Network monitoring application Name: icinga2 -Version: 2.4.7 +Version: 2.4.8 Release: %{revision}%{?dist} License: GPL-2.0+ Group: Applications/System diff --git a/tools/chocolateyInstall.ps1 b/tools/chocolateyInstall.ps1 index b31c84f24..034446038 100755 --- a/tools/chocolateyInstall.ps1 +++ b/tools/chocolateyInstall.ps1 @@ -1,7 +1,7 @@ $packageName = 'icinga2' $installerType = 'msi' -$url32 = 'http://packages.icinga.org/windows/Icinga2-v2.4.7-x86.msi' -$url64 = 'http://packages.icinga.org/windows/Icinga2-v2.4.7-x86_64.msi' +$url32 = 'http://packages.icinga.org/windows/Icinga2-v2.4.8-x86.msi' +$url64 = 'http://packages.icinga.org/windows/Icinga2-v2.4.8-x86_64.msi' $silentArgs = '/qn /norestart' $validExitCodes = @(0) From eadd61c7e4722085a6cfa6fa69a18d7170cc0c5e Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 17 May 2016 10:32:38 +0200 Subject: [PATCH 42/42] Fix PostgreSQL warnings on startup fixes #11798 --- lib/db_ido_mysql/idomysqlconnection.cpp | 4 ++-- lib/db_ido_pgsql/idopgsqlconnection.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/db_ido_mysql/idomysqlconnection.cpp b/lib/db_ido_mysql/idomysqlconnection.cpp index a71e7b389..fefaa9183 100644 --- a/lib/db_ido_mysql/idomysqlconnection.cpp +++ b/lib/db_ido_mysql/idomysqlconnection.cpp @@ -337,6 +337,8 @@ void IdoMysqlConnection::Reconnect(void) /* set session time zone to utc */ Query("SET SESSION TIME_ZONE='+00:00'"); + Query("BEGIN"); + /* update programstatus table */ UpdateProgramStatus(); @@ -370,8 +372,6 @@ void IdoMysqlConnection::Reconnect(void) activeDbObjs.push_back(dbobj); } - Query("BEGIN"); - BOOST_FOREACH(const DbObject::Ptr& dbobj, activeDbObjs) { if (dbobj->GetObject() == NULL) { Log(LogNotice, "IdoMysqlConnection") diff --git a/lib/db_ido_pgsql/idopgsqlconnection.cpp b/lib/db_ido_pgsql/idopgsqlconnection.cpp index 93f72f9d1..6bd6f0332 100644 --- a/lib/db_ido_pgsql/idopgsqlconnection.cpp +++ b/lib/db_ido_pgsql/idopgsqlconnection.cpp @@ -324,6 +324,8 @@ void IdoPgsqlConnection::Reconnect(void) Log(LogInformation, "IdoPgsqlConnection") << "pgSQL IDO instance id: " << static_cast(m_InstanceID) << " (schema version: '" + version + "')"; + Query("BEGIN"); + /* update programstatus table */ UpdateProgramStatus(); @@ -360,8 +362,6 @@ void IdoPgsqlConnection::Reconnect(void) activeDbObjs.push_back(dbobj); } - Query("BEGIN"); - BOOST_FOREACH(const DbObject::Ptr& dbobj, activeDbObjs) { if (dbobj->GetObject() == NULL) { Log(LogNotice, "IdoPgsqlConnection")